v20201116
authorKilian Saffran <ksaffran@dks.lu>
Mon, 16 Nov 2020 07:07:07 +0000 (08:07 +0100)
committerKilian Saffran <ksaffran@dks.lu>
Mon, 16 Nov 2020 07:07:07 +0000 (08:07 +0100)
32 files changed:
admin/lib/web/rendezvous.php
website/css/style.css
website/css/theme.css
website/db.php [new file with mode: 0644]
website/img/appicon.png [new file with mode: 0644]
website/index.example.html [deleted file]
website/index.php
website/js/dataform.js [new file with mode: 0644]
website/js/gallery.js
website/js/index.js [new file with mode: 0644]
website/js/myapp.js [new file with mode: 0644]
website/js/rendezvous.js
website/js/request.js [new file with mode: 0644]
website/js/site.js [deleted file]
website/js/sw.js
website/js/vendor/photoswipe/default-skin/default-skin.css [new file with mode: 0644]
website/js/vendor/photoswipe/default-skin/default-skin.png [new file with mode: 0644]
website/js/vendor/photoswipe/default-skin/default-skin.svg [new file with mode: 0644]
website/js/vendor/photoswipe/default-skin/preloader.gif [new file with mode: 0644]
website/js/vendor/photoswipe/photoswipe-ui-default.js [new file with mode: 0644]
website/js/vendor/photoswipe/photoswipe-ui-default.min.js [new file with mode: 0644]
website/js/vendor/photoswipe/photoswipe.css [new file with mode: 0644]
website/js/vendor/photoswipe/photoswipe.js [new file with mode: 0644]
website/js/vendor/photoswipe/photoswipe.min.js [new file with mode: 0644]
website/lib/config.php
website/lib/database.php
website/lib/modules/RendezVous.php [new file with mode: 0644]
website/manifest.webmanifest [new file with mode: 0644]
website/tmpl/index.html.mustache
website/tmpl/pages/gallery.html.mustache
website/tmpl/pages/index.html.mustache [moved from website/tmpl/pages/home.html.mustache with 85% similarity]
website/tmpl/pages/rendezvous.html.mustache

index 86cb73b..70dd8af 100644 (file)
@@ -28,9 +28,7 @@ class rendezvous {
 
 
 
-  public function __destruct(){
-    $this->dbh = null;
-  }
+  
 }
 
 ?>
\ No newline at end of file
index 93157a5..0a029b4 100644 (file)
@@ -3,14 +3,14 @@
 } */
 
 /* Make content area fill the entire browser window */
-html,
+/* html,
 .fullscreen {
   display: flex;
   height: 100%;
   margin: 0;
   padding: 0;
   width: 100%;
-}
+} */
 
 /* Center the content in the browser window
 .container {
index e1e6f53..48dfcdf 100644 (file)
@@ -162,6 +162,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
 .hover-none:hover{box-shadow:none!important}
 /* DEFAULT COLORS */
 .red-gold,.hover-red-gold{color: #b0834c;background-color: #c42027;}
+.red-white,.hover-red-white{color: #fff;background-color: #c42027;}
 .gold-white,.hover-gold-white{background-color: #b0834c;color: #fff;}
 .text-white { color: #fff;}
 /* .label { color: #000; font-size: 8pt;} */
diff --git a/website/db.php b/website/db.php
new file mode 100644 (file)
index 0000000..8d2fd58
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+   require("lib/config.php");
+   require("lib/database.php");
+   require("lib/modules/RendezVous.php");
+
+   $db = new database($cfg["db"]);
+   $vars = array();
+   $html = array();
+
+   $params = array();
+   $params = json_decode(file_get_contents('php://input'), true);
+   $html["params"] = $params;
+   if (isset($params["get"])){
+    if (strpos($params["get"], 'rdv_') === 0) {
+      $rdv = new RendezVous($db);
+      if ($params["get"] == 'rdv_services'){
+        $html["data"] = $rdv->getServices(); 
+      } elseif ($params["get"] == 'rdv_dates'){
+        $html["data"] = $rdv->getRDVDates();
+      } elseif ($params["get"] == 'rdv_freehours'){
+        $html["data"] = $rdv->getRDVDayFreeTimes($db->securetext($params->{daydate}));
+      }
+    } elseif (strpos($params["get"], 'gallery_') === 0){
+
+    } elseif (strpos($params["get"], 'shop_') === 0){
+      
+    }  
+
+  } elseif (isset($params["fn"])){
+    if (strpos($params["fn"], 'rdv_') === 0) {
+      $rdv = new RendezVous($db);
+      if ($params["fn"] == 'rdv_confirm'){
+
+      }elseif ($params["fn"] == 'rdv_cancel'){
+        
+      }
+    }
+
+
+  } elseif(isset($params["save"])){
+
+  }
+  header('Content-Type: application/json');
+  echo json_encode($html);
+?>
\ No newline at end of file
diff --git a/website/img/appicon.png b/website/img/appicon.png
new file mode 100644 (file)
index 0000000..c8e5131
Binary files /dev/null and b/website/img/appicon.png differ
diff --git a/website/index.example.html b/website/index.example.html
deleted file mode 100644 (file)
index 5a7c656..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
-  <meta charset="utf-8">
-  <title>Hello World</title>
-  <link rel="manifest" href="/manifest.json">
-  <link rel="stylesheet" href="css/style.css">
-  <link rel="icon" href="favicon.ico" type="image/x-icon" />
-  <link rel="apple-touch-icon" href="images/hello-icon-152.png">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <meta name="theme-color" content="white"/>
-  <meta name="apple-mobile-web-app-capable" content="yes">
-  <meta name="apple-mobile-web-app-status-bar-style" content="black">
-  <meta name="apple-mobile-web-app-title" content="Hello World">
-  <meta name="msapplication-TileImage" content="images/hello-icon-144.png">
-  <meta name="msapplication-TileColor" content="#FFFFFF">
-</head>
-<body class="fullscreen">
-  <div class="container">
-    <h1 class="title">Hello World!</h1>
-  </div>
-  <script src="js/main.js"></script>
-</body>
-</html>
index 8255193..95553c9 100644 (file)
@@ -6,7 +6,7 @@
   require("lib/database.php");
   
   $vars = array();
-  $vars["page"] = 'home.html';
+  $vars["page"] = 'index.html';
   if ($_SERVER["REQUEST_URI"] != $cfg["basepath"]){
     if (strpos($_SERVER["REQUEST_URI"], ".html?") === false){
       $vars["filepath"] = $_SERVER["REQUEST_URI"];
@@ -14,7 +14,7 @@
       $vars["filepath"] = substr($_SERVER["REQUEST_URI"],0,strpos($_SERVER["REQUEST_URI"],".html?")).".html";
     }
     
-    $vars["params"] = getparams();
+    //$vars["params"] = getparams();
     $vars["page"] = substr($vars["filepath"],strlen($cfg["basepath"]));
   }
   $vars["contenttype"] = "text/html";
@@ -63,5 +63,5 @@
 
   $mainsite = $m->render('index.html',$vars);
   echo $mainsite;
-  //echo "<pre>".print_r($vars).print_r($_SERVER["REQUEST_URI"]).print_r($vars)."</pre>";
+  //echo "<pre>".print_r($vars).print_r($_SERVER["REQUEST_URI"]).print_r($vars)."</pre>"
 ?>
\ No newline at end of file
diff --git a/website/js/dataform.js b/website/js/dataform.js
new file mode 100644 (file)
index 0000000..ec4263f
--- /dev/null
@@ -0,0 +1,276 @@
+let dataform = {
+  formsaved: function(data){
+    var sb = document.getElementById("snackbar");
+    sb.className="show green";
+    sb.innerHTML = 'Les données ont été sauvegardées!';
+    setTimeout(function(){ sb.className = sb.className.replace("show green", ""); }, 3000);
+    return false;
+  },
+  getformcontent: function(frmid,dataflds){
+    var frm = document.getElementById("frm_" + frmid);
+    var flds = {};
+    if (dataflds){
+      flds = dataflds;
+    } 
+    for (var i = 0; i < frm.elements.length; i++) {
+      var field = frm.elements[i];
+      if (((field.getAttribute("name") != 'null') && (field.tagName == "INPUT" || field.tagName == "SELECT" || field.tagName == "TEXTAREA")) && (field.id)){
+        // if (field.classList.contains("tagedit")){
+        //   var fvalue=field.value.trim();
+        //   var ndata = null;
+        //   if (fvalue != ""){
+        //     ndata =  fvalue.split(",");  
+        //   }
+        //   flds[field.getAttribute("name")] = ndata;
+        // }
+        if (field.tagName == "TEXTAREA" ){
+          if (field.classList.contains("richeditarea")){
+            flds[field.dataset.table][field.dataset.column] = tinymce.get(field.id).getContent();
+          } else {
+            flds[field.dataset.table][field.dataset.column] = field.innerHTML;
+          }
+        } else if (field.type == "checkbox" ){
+          if (field.checked){
+            flds[field.dataset.table][field.dataset.column] = "1";
+          } else {
+            flds[field.dataset.table][field.dataset.column] = "";
+          }
+        }
+        else if (field.tagName == "SELECT" && field.multiple == true){
+            var opts = field.selectedOptions;
+            var vals = [];
+            for (var o in opts){
+              if (opts[o].value){
+                vals.push(opts[o].value);
+              }
+            }
+            if (vals.length > 0) {
+              flds[field.dataset.table][field.dataset.column] = JSON.stringify(vals);
+            } else {
+              flds[field.dataset.table][field.dataset.column] = "";
+            }    
+        } 
+        else {
+          console.log("Field",field,field.id, field.value);
+          flds[field.dataset.table][field.dataset.column] = field.value;
+        }
+      }
+    }
+    return flds;
+  },
+  cleanform: function(frmname){
+    var frm = document.getElementById("frm_" + frmname);
+    for (var f in frm){
+      if (frm[f] && frm[f].id){
+      if (frm[f].tagName == 'INPUT'){
+        if (frm[f].type == "checkbox"){
+          frm[f].checked = false;
+        } else if (frm[f].classList.contains("datefield")){
+            if (frm[f]._flatpickr){ frm[f]._flatpickr.clear(); }
+        } else if (frm[f].classList.contains("choices__input")){
+          if (choice[frmname][frm[f].id]){
+            choice[frmname][frm[f].id].removeActiveItems();
+          }
+        } else {
+          frm[f].value = "";
+        }
+      }
+      if (frm[f].tagName == 'SELECT'){
+        if (frm[f].multiple == true){
+          if (frm[f].classList.contains("choices__input")){
+            choice[frmname][frm[f].id].removeActiveItems();
+          }
+        } else {
+          frm[f].value = "";
+        }
+        
+      }
+      if (frm[f].tagName == 'TEXTAREA'){
+        if (frm[f].classList.contains("richeditarea")){
+          tinymce.get(frm[f].id).setContent("");
+        } else {
+          frm[f].innerHTML = "";
+        } 
+      }
+    }
+    }
+    return false;
+  },
+  // cleanform2: function(frmname,choices){
+  //   var frm = document.getElementById("frm_" + frmname);
+  //   for (var f in frm){
+  //     if (frm[f] && frm[f].id){
+  //     if (frm[f].tagName == 'INPUT'){
+  //       if (frm[f].type == "checkbox"){
+  //         frm[f].checked = false;
+  //       } else if (frm[f].classList.contains("datefield")){
+  //           if (frm[f]._flatpickr){ frm[f]._flatpickr.clear(); }
+  //       } else if (frm[f].classList.contains("choices__input")){
+  //         if (choices[frm[f].id]){
+  //           choices[frm[f].id].removeActiveItems();
+  //         }
+  //       } else {
+  //         frm[f].value = "";
+  //       }
+  //     }
+  //     if (frm[f].tagName == 'SELECT'){
+  //       if (frm[f].multiple == true){
+  //         if (frm[f].classList.contains("choices__input")){
+  //           choices[frm[f].id].removeActiveItems();
+  //         }
+  //       } else {
+  //         frm[f].value = "";
+  //       }
+        
+  //     }
+  //     if (frm[f].tagName == 'TEXTAREA'){
+  //       if (frm[f].classList.contains("richeditarea")){
+  //         tinymce.get(frm[f].id).setContent("");
+  //       } else {
+  //         frm[f].innerHTML = "";
+  //       } 
+  //     }
+  //   }
+  //   }
+  //   return false;
+  // },
+  fillform: function(dataclass,choices,data){
+    var frm = document.querySelectorAll('.data_'+ dataclass);
+    //console.log("frmdata",frm);
+    if (data){
+      for (var f in frm){
+        
+        var tblid = null;
+        //console.log("ID",frm[f].id);
+        if (frm[f].id){
+          tblid = frm[f].id.replace(dataclass + "_","");
+          frm[f].dataset.id=data['id'];
+        }
+        //console.log(frm[f].id +'=>' +tblid + "=>" +data[tblid]);
+        if (tblid != null && data[tblid]){
+  
+          if (frm[f].tagName == 'INPUT'){
+            if (frm[f].type == "checkbox"){
+              if (data[tblid] == "1"){
+                frm[f].checked = true;
+              } else {
+                frm[f].checked = false;
+              }
+            } 
+            else if (frm[f].classList.contains("datefield")){ 
+              frm[f]._flatpickr.setDate(data[tblid]);
+            } 
+            else if (frm[f].classList.contains("timefield")){
+              frm[f].value = timecalc.StringToTime(data[tblid]);
+            } 
+            else if (frm[f].classList.contains("number")){
+              frm[f].value = dataform.setNumber(data[tblid],null);
+            }
+            else if (frm[f].classList.contains("currency")){
+              frm[f].value = dataform.setNumber(data[tblid],2);
+            }
+            else if (frm[f].classList.contains("percent")){
+              frm[f].value = dataform.setNumber(data[tblid],2);
+            }
+            else {
+              frm[f].value=data[tblid];
+            }
+          }
+          if (frm[f].tagName == 'SELECT'){
+            if (frm[f].multiple == true){
+         
+              choices[frm[f].id].set(JSON.parse(data[tblid]));
+            } else {
+              frm[f].value=data[tblid];
+            }
+          }
+          if (frm[f].tagName == "TEXTAREA"){
+            //console.log("Set TextArea: " + frm[f].id + " => "+ data[tblid]);
+            if (frm[f].classList.contains("richeditarea")){
+              
+              tinymce.get(frm[f].id).setContent(data[tblid]);
+            } else {
+              frm[f].value = data[tblid];
+            } 
+          }
+        } 
+      }
+    }
+  },
+  setNumber(numdata,decimals){
+    console.log("Number in ",numdata);
+    if (numdata == null || numdata == ''){
+      return "";
+    } 
+    if (decimals){
+      numdata = parseFloat(numdata).toFixed(decimals).toString();    
+    }
+    numdata.toString().replace(/./,',');
+    console.log("Number out ",numdata);
+    return numdata;
+  },
+  fillselectlist: function(obj,listdata,vidcol,vvalcol){
+    var sellist = [];
+    let csel = obj.value;
+    //console.log(obj);
+    //console.log("selectdata",listdata);
+    
+    // for (var d in listdata){
+    // console.log(d);
+    // }
+    // console.log("datasize",listdata.length);
+    if (listdata.length > 0){
+      for (var i=0;i<listdata.length;i++){
+        
+        sellist.push({value:listdata[i][vidcol],text:listdata[i][vvalcol]});
+      }
+    }
+    //console.log("Select data",sellist);
+    obj.setData(sellist);
+    if (obj.select.element.dataset.selected && obj.select.element.dataset.selected != ""){
+       obj.set(obj.select.element.dataset.selected);
+    }
+    return false;
+  },
+  filldatalist: function(obj,optdata,element){
+    obj.innerHTML = '';
+    for (d=0;d < optdata.length;d++){
+      obj.insertAdjacentHTML('beforend','<option vale="' + optdata[d][element] + '"/>');
+    }
+  },
+  setValue: function(obj,newvalue){
+    if (obj.tagName == 'INPUT' || obj.tagName == 'SELECT'){
+      if (obj.type == 'checkbox' || obj.type == 'radio'){
+        obj.checked = true;
+      }else {
+        obj.value=newvalue;
+      }
+    }
+    if (obj.tagName == 'TEXTAREA'){
+      obj.innerHTML = newvalue;
+    }
+    dataform.savefield(obj);
+  },
+  savefield: function(obj){
+    //if (obj.tagName == 'TextArea')
+    fdata = obj.dataset;
+    //fdata["save"] = "field";
+    //fdata["schemata"] = schemata;
+    if (obj.tagName == 'INPUT' || obj.tagName == 'SELECT' || obj.tagName == 'TEXTAREA'){
+      if (obj.type == 'checkbox' || obj.type == 'radio'){
+        if (obj.checked == true){
+          fdata["value"] = 1;
+        } else {
+          fdata["value"] = null;
+        }
+      }else {
+        fdata["value"] = obj.value;
+      }
+    }
+    db.exec("UPDATE "+ fdata["table"]+" SET "+fdata["column"]+"='"+ fdata["value"] + "' WHERE id='" + fdata["id"]+ "';"); 
+    return false;
+  },
+  cleanfield: function(objid){
+    document.getElementById(objid).value='';
+  },
+  }
\ No newline at end of file
index e69de29..fb3bd67 100644 (file)
@@ -0,0 +1,7 @@
+function initpage(){
+  
+}
+
+let gallery = {
+
+}
\ No newline at end of file
diff --git a/website/js/index.js b/website/js/index.js
new file mode 100644 (file)
index 0000000..528feae
--- /dev/null
@@ -0,0 +1,37 @@
+if ('serviceWorker' in navigator) {
+  navigator.serviceWorker
+           .register('./js/sw.js');
+}
+
+let deferredPrompt;
+const addBtn = document.querySelector('.add-button');
+addBtn.style.display = 'none';
+
+window.addEventListener('beforeinstallprompt', (e) => {
+// Prevent Chrome 67 and earlier from automatically showing the prompt
+e.preventDefault();
+// Stash the event so it can be triggered later.
+deferredPrompt = e;
+// Update UI to notify the user they can add to home screen
+addBtn.style.display = 'block';
+
+addBtn.addEventListener('click', (e) => {
+  // hide our user interface that shows our A2HS button
+  addBtn.style.display = 'none';
+  // Show the prompt
+  deferredPrompt.prompt();
+  // Wait for the user to respond to the prompt
+  deferredPrompt.userChoice.then((choiceResult) => {
+      if (choiceResult.outcome === 'accepted') {
+        console.log('User accepted the A2HS prompt');
+      } else {
+        console.log('User dismissed the A2HS prompt');
+      }
+      deferredPrompt = null;
+    });
+});
+});
+
+function initpage(){
+  
+}
\ No newline at end of file
diff --git a/website/js/myapp.js b/website/js/myapp.js
new file mode 100644 (file)
index 0000000..4eb5749
--- /dev/null
@@ -0,0 +1,113 @@
+// function togglemenu(){
+//   var mnu = document.getElementById("mobilemenu");
+//   if (mnu.style.display == 'none'){
+//     mnu.style.display = 'block';
+//   } else {
+//     mnu.style.display = 'none';
+//   }
+// }
+
+let myapp={
+  loadpanel: function(idpanel){
+    return new Promise(
+      function(resolve, reject){
+      if (document.getElementById("pnl_" + idpanel).innerHTML == ''){
+        var pnldoc = usersystem.readAppFile('panels/' + idpanel + '.html');
+        if (pnldoc == ""){
+          reject('panels/' + idpanel + '.html' + " data is empty");
+        }else {
+          document.getElementById("pnl_" + idpanel).insertAdjacentHTML('afterbegin', pnldoc);
+          if (eval("typeof "+ idpanel) == "object"){
+            eval(`${idpanel}.init();`);
+          }
+          resolve(true);
+        }
+      } else {
+        resolve(true);
+      }
+    });
+  },
+  viewpanel: function( idpanel){
+    myapp.loadpanel(idpanel).then(result => {
+      if (result){
+        let panels = document.getElementsByClassName("panel");
+        for (let i=0;i<panels.length;i++){
+          panels[i].style.display = 'none';
+        }
+        document.getElementById("pnl_" + idpanel).style.display = 'block';
+      }
+    });
+  },
+  loaddialog: function( dlgname ){
+    return new Promise(
+      function(resolve, reject){
+        if (!document.getElementById("dlg" + dlgname)){
+          var dlgdoc = usersystem.readAppFile('dialogs/' + dlgname + '.html');
+          if (dlgdoc == ""){
+            console.log('dialogs/' + dlgname + '.html' + " data is empty");
+            reject(false);
+          } else {
+            document.getElementById("dialogs").insertAdjacentHTML('beforeend', dlgdoc);
+            resolve(true);
+          }
+          
+        }
+    });
+  },
+  viewdialog: function( dlgname){
+    //app.loaddialog(dlgname).then(result => {
+      document.getElementById("dlg" + dlgname).style.display = 'block';
+    //});
+    
+  },
+  closeDialog: function(dlgname){
+    document.getElementById("dlg" + dlgname).style.display = 'none';
+  },
+  confirm(title,message,strbtnok,strbtncancel,action){
+    document.getElementById("dlgconfirm_title").innerHTML = title;
+    document.getElementById("dlgconfirm_text").innerHTML = message;
+    document.getElementById("dlgconfirm_btnok").innerHTML = strbtnok;
+    document.getElementById("dlgconfirm_btncancel").innerHTML = strbtncancel;
+    document.getElementById("dlgconfirm_btnok").setAttribute("onclick","document.getElementById('dlgconfirm').style.display='none';" + action);
+    document.getElementById("dlgconfirm").style.display = 'block';
+  },
+  message: function(title,message){
+    document.getElementById('dlgmessage_title').innerHTML=title;
+    document.getElementById('dlgmessage_text').innerHTML=message;
+    document.getElementById('dlgmessage').style.display='block';
+    return false;
+  }
+}
+
+document.addEventListener("DOMContentLoaded", function() {
+  initpage();
+});
+
+// function hideAddressBar(){
+//   if(document.documentElement.scrollHeight<window.outerHeight/window.devicePixelRatio)
+//     document.documentElement.style.height=(window.outerHeight/window.devicePixelRatio)+'px';
+//   setTimeout(window.scrollTo(1,1),0);
+// }
+// window.addEventListener("load",function(){hideAddressBar();});
+// window.addEventListener("orientationchange",function(){hideAddressBar();});
+
+
+
+
+// function dropdown(drdid){
+//   var x = document.getElementById(drdid);
+//   if (x.className.indexOf("show") == -1) {
+//     x.className += " show";
+//   } else { 
+//     x.className = x.className.replace(" show", "");
+//   }
+// }
+
+// function dropup(drdid){
+//   var x = document.getElementById(drdid);
+//   if (x.className.indexOf("show") == -1) {
+//     x.className += " show";
+//   } else { 
+//     x.className = x.className.replace(" show", "");
+//   }
+// }
\ No newline at end of file
index ec2700f..81131a5 100644 (file)
@@ -1,11 +1,98 @@
+function initpage(){
+  rendezvous.init();
+}
+
 let rendezvous = {
-  tblproducts: null,
+  tblservices: null,
   tbldates: null,
   tblhours:null,
-  viewpanel: function(id){
-
-  },
+  data:{"id":null,"products":null,"daydate":null,"startime":null,"clientname":null,"clientemail":null,"clientphone":null,"clientmessage":null},
   init: function(){
+    rendezvous.tblservices = new Tabulator("#tbl_rdvservice",{
+      height: "calc(100vh - 70px)",
+      layout: "fitDataStretch",
+      selectable: true,
+      // rowClick:function(e, row){
+      //   console.log("Filter selected");
+      //   let fsel = addresses.tbl.getSelectedData();
+      //   console.log(fsel);
+      //   addresses.current_id = fsel[0].id;
+      //   addresses.edit(fsel[0].id);
+      //   //invoices.getsums(fsel);
+      // },
+      rowContext:function(e, row){ e.preventDefault(); },
+        columns: [
+          {title: "Services", field: "product", resizable: false}, 
+      ]
+    });
+    rendezvous.tbldates = new Tabulator("#tbl_rdvdate",{
+      height: "calc(100vh - 70px)",
+      layout: "fitDataStretch",
+      selectable: 1,
+      rowContext:function(e, row){ e.preventDefault(); },
+        columns: [
+          {title: "Dates disponibles", field: "daydates", resizable: false,formatter:"datetime",headerSort:true,formatterParams:{inputFormat:"YYYY-MM-DD",outputFormat:"DD.MM.YYYY",invalidPlaceholder:""}}, 
+      ]
+    });
+    rendezvous.tblhours = new Tabulator("#tbl_rdvtime",{
+      height: "calc(100vh - 70px)",
+      layout: "fitDataStretch",
+      selectable: 1,
+      rowContext:function(e, row){ e.preventDefault(); },
+        columns: [
+          {title: "heures disponibles", field: "starttime", resizable: false}, 
+      ]
+    });
+    rendezvous.getServiceData();
+  },
+  viewServices: function(){
+    myapp.viewpanel('rdvservice');
+  },
+  viewDates: function(){
+    let sel = rendezvous.tblservices.getSelectedData();
+    if (sel[0]){
+      postData({"get":"rdv_dates"}).then(data => {
+        rendezvous.tbldates.setData(data.data);
+        myapp.viewpanel('rdvdate');
+      });
+    }
+  },
+  viewHours: function(){
+    let sel = rendezvous.tbldates.getSelectedData();
+    if (sel[0]){
+      postData({"get":"rdv_freehours"}).then(data => {
+        rendezvous.tblhours.setData(data.data);
+      });
+      myapp.viewpanel('rdvtime');
+    }
+  },
+  viewClientData: function(){
+    let sel = rendezvous.tblhours.getSelectedData();
+    if (sel[0]){
+      myapp.viewpanel('rdvclientdata');
+    }
+  },
+  viewConfirmation: function(){
+    document.getElementById("rendezvous_clientname").value;
+    if ()
+    myapp.viewpanel('rdvconfirm');
+  },
+  viewSended: function(){
+    myapp.viewpanel('rdvsended');
+  },
+  confirm: function(){
+    myapp.viewpanel('rdvsended');
+  },
+  cancel: function(){
+    myapp.viewpanel('rdvsended');
+  },
+  getServiceData: function(){
+    postData({"get":"rdv_services"}).then(data => {
+      console.log(data);
+      rendezvous.tblservices.setData(data.data);
+    });
+  },
+  getHourData: function(){
     
-  }
+  },
 }
\ No newline at end of file
diff --git a/website/js/request.js b/website/js/request.js
new file mode 100644 (file)
index 0000000..a168798
--- /dev/null
@@ -0,0 +1,19 @@
+let api = location.origin + '/oldbell_lu/';
+console.log(api);
+async function postData(data = {}) {
+  // Default options are marked with *
+  const response = await fetch(api + 'db.php', {
+    method: 'POST', 
+    mode: 'same-origin', 
+    cache: 'no-cache', 
+    credentials: 'same-origin', 
+    headers: {
+      'Content-Type': 'application/json'
+      
+    },
+    redirect: 'follow', 
+    referrerPolicy: 'strict-origin', 
+    body: JSON.stringify(data) 
+  });
+  return response.json(); 
+}
\ No newline at end of file
diff --git a/website/js/site.js b/website/js/site.js
deleted file mode 100644 (file)
index d3b454a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// function togglemenu(){
-//   var mnu = document.getElementById("mobilemenu");
-//   if (mnu.style.display == 'none'){
-//     mnu.style.display = 'block';
-//   } else {
-//     mnu.style.display = 'none';
-//   }
-// }
-
-function hideAddressBar(){
-  if(document.documentElement.scrollHeight<window.outerHeight/window.devicePixelRatio)
-    document.documentElement.style.height=(window.outerHeight/window.devicePixelRatio)+'px';
-  setTimeout(window.scrollTo(1,1),0);
-}
-window.addEventListener("load",function(){hideAddressBar();});
-window.addEventListener("orientationchange",function(){hideAddressBar();});
-
-window.onload = () => {
-  'use strict';
-
-  if ('serviceWorker' in navigator) {
-    navigator.serviceWorker
-             .register('./js/sw.js');
-  }
-}
-
-function dropdown(drdid){
-  var x = document.getElementById(drdid);
-  if (x.className.indexOf("show") == -1) {
-    x.className += " show";
-  } else { 
-    x.className = x.className.replace(" show", "");
-  }
-}
-
-function dropup(drdid){
-  var x = document.getElementById(drdid);
-  if (x.className.indexOf("show") == -1) {
-    x.className += " show";
-  } else { 
-    x.className = x.className.replace(" show", "");
-  }
-}
\ No newline at end of file
index 872158d..8b2bb43 100644 (file)
@@ -1,14 +1,6 @@
 var cacheName = 'oldbell-pwa';
 var filesToCache = [
-  '/oldbell_lu/',
-  '/oldbell_lu/index.html',
-  '/oldbell_lu/css/theme.css',
-  '/oldbell_lu/css/icons.css',
-  '/oldbell_lu/css/fonts/siteicons.svg',
-  '/oldbell_lu/css/fonts/siteicons.ttf',
-  '/oldbell_lu/css/fonts/siteicons.woff',
-  '/oldbell_lu/css/theme.css',
-  '/oldbell_lu/js/site.js'
+  '/oldbell_lu/index.html'
 ];
 
 /* Start the service worker and cache all of the app's content */
diff --git a/website/js/vendor/photoswipe/default-skin/default-skin.css b/website/js/vendor/photoswipe/default-skin/default-skin.css
new file mode 100644 (file)
index 0000000..c961632
--- /dev/null
@@ -0,0 +1,482 @@
+/*! PhotoSwipe Default UI CSS by Dmitry Semenov | photoswipe.com | MIT license */
+/*
+
+       Contents:
+
+       1. Buttons
+       2. Share modal and links
+       3. Index indicator ("1 of X" counter)
+       4. Caption
+       5. Loading indicator
+       6. Additional styles (root element, top bar, idle state, hidden state, etc.)
+
+*/
+/*
+       
+       1. Buttons
+
+ */
+/* <button> css reset */
+.pswp__button {
+  width: 44px;
+  height: 44px;
+  position: relative;
+  background: none;
+  cursor: pointer;
+  overflow: visible;
+  -webkit-appearance: none;
+  display: block;
+  border: 0;
+  padding: 0;
+  margin: 0;
+  float: right;
+  opacity: 0.75;
+  -webkit-transition: opacity 0.2s;
+          transition: opacity 0.2s;
+  -webkit-box-shadow: none;
+          box-shadow: none; }
+  .pswp__button:focus, .pswp__button:hover {
+    opacity: 1; }
+  .pswp__button:active {
+    outline: none;
+    opacity: 0.9; }
+  .pswp__button::-moz-focus-inner {
+    padding: 0;
+    border: 0; }
+
+/* pswp__ui--over-close class it added when mouse is over element that should close gallery */
+.pswp__ui--over-close .pswp__button--close {
+  opacity: 1; }
+
+.pswp__button,
+.pswp__button--arrow--left:before,
+.pswp__button--arrow--right:before {
+  background: url(default-skin.png) 0 0 no-repeat;
+  background-size: 264px 88px;
+  width: 44px;
+  height: 44px; }
+
+@media (-webkit-min-device-pixel-ratio: 1.1), (-webkit-min-device-pixel-ratio: 1.09375), (min-resolution: 105dpi), (min-resolution: 1.1dppx) {
+  /* Serve SVG sprite if browser supports SVG and resolution is more than 105dpi */
+  .pswp--svg .pswp__button,
+  .pswp--svg .pswp__button--arrow--left:before,
+  .pswp--svg .pswp__button--arrow--right:before {
+    background-image: url(default-skin.svg); }
+  .pswp--svg .pswp__button--arrow--left,
+  .pswp--svg .pswp__button--arrow--right {
+    background: none; } }
+
+.pswp__button--close {
+  background-position: 0 -44px; }
+
+.pswp__button--share {
+  background-position: -44px -44px; }
+
+.pswp__button--fs {
+  display: none; }
+
+.pswp--supports-fs .pswp__button--fs {
+  display: block; }
+
+.pswp--fs .pswp__button--fs {
+  background-position: -44px 0; }
+
+.pswp__button--zoom {
+  display: none;
+  background-position: -88px 0; }
+
+.pswp--zoom-allowed .pswp__button--zoom {
+  display: block; }
+
+.pswp--zoomed-in .pswp__button--zoom {
+  background-position: -132px 0; }
+
+/* no arrows on touch screens */
+.pswp--touch .pswp__button--arrow--left,
+.pswp--touch .pswp__button--arrow--right {
+  visibility: hidden; }
+
+/*
+       Arrow buttons hit area
+       (icon is added to :before pseudo-element)
+*/
+.pswp__button--arrow--left,
+.pswp__button--arrow--right {
+  background: none;
+  top: 50%;
+  margin-top: -50px;
+  width: 70px;
+  height: 100px;
+  position: absolute; }
+
+.pswp__button--arrow--left {
+  left: 0; }
+
+.pswp__button--arrow--right {
+  right: 0; }
+
+.pswp__button--arrow--left:before,
+.pswp__button--arrow--right:before {
+  content: '';
+  top: 35px;
+  background-color: rgba(0, 0, 0, 0.3);
+  height: 30px;
+  width: 32px;
+  position: absolute; }
+
+.pswp__button--arrow--left:before {
+  left: 6px;
+  background-position: -138px -44px; }
+
+.pswp__button--arrow--right:before {
+  right: 6px;
+  background-position: -94px -44px; }
+
+/*
+
+       2. Share modal/popup and links
+
+ */
+.pswp__counter,
+.pswp__share-modal {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+      user-select: none; }
+
+.pswp__share-modal {
+  display: block;
+  background: rgba(0, 0, 0, 0.5);
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  padding: 10px;
+  position: absolute;
+  z-index: 1600;
+  opacity: 0;
+  -webkit-transition: opacity 0.25s ease-out;
+          transition: opacity 0.25s ease-out;
+  -webkit-backface-visibility: hidden;
+  will-change: opacity; }
+
+.pswp__share-modal--hidden {
+  display: none; }
+
+.pswp__share-tooltip {
+  z-index: 1620;
+  position: absolute;
+  background: #FFF;
+  top: 56px;
+  border-radius: 2px;
+  display: block;
+  width: auto;
+  right: 44px;
+  -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
+          box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
+  -webkit-transform: translateY(6px);
+      -ms-transform: translateY(6px);
+          transform: translateY(6px);
+  -webkit-transition: -webkit-transform 0.25s;
+          transition: transform 0.25s;
+  -webkit-backface-visibility: hidden;
+  will-change: transform; }
+  .pswp__share-tooltip a {
+    display: block;
+    padding: 8px 12px;
+    color: #000;
+    text-decoration: none;
+    font-size: 14px;
+    line-height: 18px; }
+    .pswp__share-tooltip a:hover {
+      text-decoration: none;
+      color: #000; }
+    .pswp__share-tooltip a:first-child {
+      /* round corners on the first/last list item */
+      border-radius: 2px 2px 0 0; }
+    .pswp__share-tooltip a:last-child {
+      border-radius: 0 0 2px 2px; }
+
+.pswp__share-modal--fade-in {
+  opacity: 1; }
+  .pswp__share-modal--fade-in .pswp__share-tooltip {
+    -webkit-transform: translateY(0);
+        -ms-transform: translateY(0);
+            transform: translateY(0); }
+
+/* increase size of share links on touch devices */
+.pswp--touch .pswp__share-tooltip a {
+  padding: 16px 12px; }
+
+a.pswp__share--facebook:before {
+  content: '';
+  display: block;
+  width: 0;
+  height: 0;
+  position: absolute;
+  top: -12px;
+  right: 15px;
+  border: 6px solid transparent;
+  border-bottom-color: #FFF;
+  -webkit-pointer-events: none;
+  -moz-pointer-events: none;
+  pointer-events: none; }
+
+a.pswp__share--facebook:hover {
+  background: #3E5C9A;
+  color: #FFF; }
+  a.pswp__share--facebook:hover:before {
+    border-bottom-color: #3E5C9A; }
+
+a.pswp__share--twitter:hover {
+  background: #55ACEE;
+  color: #FFF; }
+
+a.pswp__share--pinterest:hover {
+  background: #CCC;
+  color: #CE272D; }
+
+a.pswp__share--download:hover {
+  background: #DDD; }
+
+/*
+
+       3. Index indicator ("1 of X" counter)
+
+ */
+.pswp__counter {
+  position: absolute;
+  left: 0;
+  top: 0;
+  height: 44px;
+  font-size: 13px;
+  line-height: 44px;
+  color: #FFF;
+  opacity: 0.75;
+  padding: 0 10px; }
+
+/*
+       
+       4. Caption
+
+ */
+.pswp__caption {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  min-height: 44px; }
+  .pswp__caption small {
+    font-size: 11px;
+    color: #BBB; }
+
+.pswp__caption__center {
+  text-align: left;
+  max-width: 420px;
+  margin: 0 auto;
+  font-size: 13px;
+  padding: 10px;
+  line-height: 20px;
+  color: #CCC; }
+
+.pswp__caption--empty {
+  display: none; }
+
+/* Fake caption element, used to calculate height of next/prev image */
+.pswp__caption--fake {
+  visibility: hidden; }
+
+/*
+
+       5. Loading indicator (preloader)
+
+       You can play with it here - http://codepen.io/dimsemenov/pen/yyBWoR
+
+ */
+.pswp__preloader {
+  width: 44px;
+  height: 44px;
+  position: absolute;
+  top: 0;
+  left: 50%;
+  margin-left: -22px;
+  opacity: 0;
+  -webkit-transition: opacity 0.25s ease-out;
+          transition: opacity 0.25s ease-out;
+  will-change: opacity;
+  direction: ltr; }
+
+.pswp__preloader__icn {
+  width: 20px;
+  height: 20px;
+  margin: 12px; }
+
+.pswp__preloader--active {
+  opacity: 1; }
+  .pswp__preloader--active .pswp__preloader__icn {
+    /* We use .gif in browsers that don't support CSS animation */
+    background: url(preloader.gif) 0 0 no-repeat; }
+
+.pswp--css_animation .pswp__preloader--active {
+  opacity: 1; }
+  .pswp--css_animation .pswp__preloader--active .pswp__preloader__icn {
+    -webkit-animation: clockwise 500ms linear infinite;
+            animation: clockwise 500ms linear infinite; }
+  .pswp--css_animation .pswp__preloader--active .pswp__preloader__donut {
+    -webkit-animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite;
+            animation: donut-rotate 1000ms cubic-bezier(0.4, 0, 0.22, 1) infinite; }
+
+.pswp--css_animation .pswp__preloader__icn {
+  background: none;
+  opacity: 0.75;
+  width: 14px;
+  height: 14px;
+  position: absolute;
+  left: 15px;
+  top: 15px;
+  margin: 0; }
+
+.pswp--css_animation .pswp__preloader__cut {
+  /* 
+                       The idea of animating inner circle is based on Polymer ("material") loading indicator 
+                        by Keanu Lee https://blog.keanulee.com/2014/10/20/the-tale-of-three-spinners.html
+               */
+  position: relative;
+  width: 7px;
+  height: 14px;
+  overflow: hidden; }
+
+.pswp--css_animation .pswp__preloader__donut {
+  -webkit-box-sizing: border-box;
+          box-sizing: border-box;
+  width: 14px;
+  height: 14px;
+  border: 2px solid #FFF;
+  border-radius: 50%;
+  border-left-color: transparent;
+  border-bottom-color: transparent;
+  position: absolute;
+  top: 0;
+  left: 0;
+  background: none;
+  margin: 0; }
+
+@media screen and (max-width: 1024px) {
+  .pswp__preloader {
+    position: relative;
+    left: auto;
+    top: auto;
+    margin: 0;
+    float: right; } }
+
+@-webkit-keyframes clockwise {
+  0% {
+    -webkit-transform: rotate(0deg);
+            transform: rotate(0deg); }
+  100% {
+    -webkit-transform: rotate(360deg);
+            transform: rotate(360deg); } }
+
+@keyframes clockwise {
+  0% {
+    -webkit-transform: rotate(0deg);
+            transform: rotate(0deg); }
+  100% {
+    -webkit-transform: rotate(360deg);
+            transform: rotate(360deg); } }
+
+@-webkit-keyframes donut-rotate {
+  0% {
+    -webkit-transform: rotate(0);
+            transform: rotate(0); }
+  50% {
+    -webkit-transform: rotate(-140deg);
+            transform: rotate(-140deg); }
+  100% {
+    -webkit-transform: rotate(0);
+            transform: rotate(0); } }
+
+@keyframes donut-rotate {
+  0% {
+    -webkit-transform: rotate(0);
+            transform: rotate(0); }
+  50% {
+    -webkit-transform: rotate(-140deg);
+            transform: rotate(-140deg); }
+  100% {
+    -webkit-transform: rotate(0);
+            transform: rotate(0); } }
+
+/*
+       
+       6. Additional styles
+
+ */
+/* root element of UI */
+.pswp__ui {
+  -webkit-font-smoothing: auto;
+  visibility: visible;
+  opacity: 1;
+  z-index: 1550; }
+
+/* top black bar with buttons and "1 of X" indicator */
+.pswp__top-bar {
+  position: absolute;
+  left: 0;
+  top: 0;
+  height: 44px;
+  width: 100%; }
+
+.pswp__caption,
+.pswp__top-bar,
+.pswp--has_mouse .pswp__button--arrow--left,
+.pswp--has_mouse .pswp__button--arrow--right {
+  -webkit-backface-visibility: hidden;
+  will-change: opacity;
+  -webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
+          transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
+
+/* pswp--has_mouse class is added only when two subsequent mousemove events occur */
+.pswp--has_mouse .pswp__button--arrow--left,
+.pswp--has_mouse .pswp__button--arrow--right {
+  visibility: visible; }
+
+.pswp__top-bar,
+.pswp__caption {
+  background-color: rgba(0, 0, 0, 0.5); }
+
+/* pswp__ui--fit class is added when main image "fits" between top bar and bottom bar (caption) */
+.pswp__ui--fit .pswp__top-bar,
+.pswp__ui--fit .pswp__caption {
+  background-color: rgba(0, 0, 0, 0.3); }
+
+/* pswp__ui--idle class is added when mouse isn't moving for several seconds (JS option timeToIdle) */
+.pswp__ui--idle .pswp__top-bar {
+  opacity: 0; }
+
+.pswp__ui--idle .pswp__button--arrow--left,
+.pswp__ui--idle .pswp__button--arrow--right {
+  opacity: 0; }
+
+/*
+       pswp__ui--hidden class is added when controls are hidden
+       e.g. when user taps to toggle visibility of controls
+*/
+.pswp__ui--hidden .pswp__top-bar,
+.pswp__ui--hidden .pswp__caption,
+.pswp__ui--hidden .pswp__button--arrow--left,
+.pswp__ui--hidden .pswp__button--arrow--right {
+  /* Force paint & create composition layer for controls. */
+  opacity: 0.001; }
+
+/* pswp__ui--one-slide class is added when there is just one item in gallery */
+.pswp__ui--one-slide .pswp__button--arrow--left,
+.pswp__ui--one-slide .pswp__button--arrow--right,
+.pswp__ui--one-slide .pswp__counter {
+  display: none; }
+
+.pswp__element--disabled {
+  display: none !important; }
+
+.pswp--minimal--dark .pswp__top-bar {
+  background: none; }
diff --git a/website/js/vendor/photoswipe/default-skin/default-skin.png b/website/js/vendor/photoswipe/default-skin/default-skin.png
new file mode 100644 (file)
index 0000000..441c502
Binary files /dev/null and b/website/js/vendor/photoswipe/default-skin/default-skin.png differ
diff --git a/website/js/vendor/photoswipe/default-skin/default-skin.svg b/website/js/vendor/photoswipe/default-skin/default-skin.svg
new file mode 100644 (file)
index 0000000..9d5f0c6
--- /dev/null
@@ -0,0 +1 @@
+<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>
\ No newline at end of file
diff --git a/website/js/vendor/photoswipe/default-skin/preloader.gif b/website/js/vendor/photoswipe/default-skin/preloader.gif
new file mode 100644 (file)
index 0000000..b8faa69
Binary files /dev/null and b/website/js/vendor/photoswipe/default-skin/preloader.gif differ
diff --git a/website/js/vendor/photoswipe/photoswipe-ui-default.js b/website/js/vendor/photoswipe/photoswipe-ui-default.js
new file mode 100644 (file)
index 0000000..f3733e1
--- /dev/null
@@ -0,0 +1,861 @@
+/*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08
+* http://photoswipe.com
+* Copyright (c) 2019 Dmitry Semenov; */
+/**
+*
+* UI on top of main sliding area (caption, arrows, close button, etc.).
+* Built just using public methods/properties of PhotoSwipe.
+* 
+*/
+(function (root, factory) { 
+       if (typeof define === 'function' && define.amd) {
+               define(factory);
+       } else if (typeof exports === 'object') {
+               module.exports = factory();
+       } else {
+               root.PhotoSwipeUI_Default = factory();
+       }
+})(this, function () {
+
+       'use strict';
+
+
+
+var PhotoSwipeUI_Default =
+ function(pswp, framework) {
+
+       var ui = this;
+       var _overlayUIUpdated = false,
+               _controlsVisible = true,
+               _fullscrenAPI,
+               _controls,
+               _captionContainer,
+               _fakeCaptionContainer,
+               _indexIndicator,
+               _shareButton,
+               _shareModal,
+               _shareModalHidden = true,
+               _initalCloseOnScrollValue,
+               _isIdle,
+               _listen,
+
+               _loadingIndicator,
+               _loadingIndicatorHidden,
+               _loadingIndicatorTimeout,
+
+               _galleryHasOneSlide,
+
+               _options,
+               _defaultUIOptions = {
+                       barsSize: {top:44, bottom:'auto'},
+                       closeElClasses: ['item', 'caption', 'zoom-wrap', 'ui', 'top-bar'], 
+                       timeToIdle: 4000, 
+                       timeToIdleOutside: 1000,
+                       loadingIndicatorDelay: 1000, // 2s
+                       
+                       addCaptionHTMLFn: function(item, captionEl /*, isFake */) {
+                               if(!item.title) {
+                                       captionEl.children[0].innerHTML = '';
+                                       return false;
+                               }
+                               captionEl.children[0].innerHTML = item.title;
+                               return true;
+                       },
+
+                       closeEl:true,
+                       captionEl: true,
+                       fullscreenEl: true,
+                       zoomEl: true,
+                       shareEl: true,
+                       counterEl: true,
+                       arrowEl: true,
+                       preloaderEl: true,
+
+                       tapToClose: false,
+                       tapToToggleControls: true,
+
+                       clickToCloseNonZoomable: true,
+
+                       shareButtons: [
+                               {id:'facebook', label:'Share on Facebook', url:'https://www.facebook.com/sharer/sharer.php?u={{url}}'},
+                               {id:'twitter', label:'Tweet', url:'https://twitter.com/intent/tweet?text={{text}}&url={{url}}'},
+                               {id:'pinterest', label:'Pin it', url:'http://www.pinterest.com/pin/create/button/'+
+                                                                                                       '?url={{url}}&media={{image_url}}&description={{text}}'},
+                               {id:'download', label:'Download image', url:'{{raw_image_url}}', download:true}
+                       ],
+                       getImageURLForShare: function( /* shareButtonData */ ) {
+                               return pswp.currItem.src || '';
+                       },
+                       getPageURLForShare: function( /* shareButtonData */ ) {
+                               return window.location.href;
+                       },
+                       getTextForShare: function( /* shareButtonData */ ) {
+                               return pswp.currItem.title || '';
+                       },
+                               
+                       indexIndicatorSep: ' / ',
+                       fitControlsWidth: 1200
+
+               },
+               _blockControlsTap,
+               _blockControlsTapTimeout;
+
+
+
+       var _onControlsTap = function(e) {
+                       if(_blockControlsTap) {
+                               return true;
+                       }
+
+
+                       e = e || window.event;
+
+                       if(_options.timeToIdle && _options.mouseUsed && !_isIdle) {
+                               // reset idle timer
+                               _onIdleMouseMove();
+                       }
+
+
+                       var target = e.target || e.srcElement,
+                               uiElement,
+                               clickedClass = target.getAttribute('class') || '',
+                               found;
+
+                       for(var i = 0; i < _uiElements.length; i++) {
+                               uiElement = _uiElements[i];
+                               if(uiElement.onTap && clickedClass.indexOf('pswp__' + uiElement.name ) > -1 ) {
+                                       uiElement.onTap();
+                                       found = true;
+
+                               }
+                       }
+
+                       if(found) {
+                               if(e.stopPropagation) {
+                                       e.stopPropagation();
+                               }
+                               _blockControlsTap = true;
+
+                               // Some versions of Android don't prevent ghost click event 
+                               // when preventDefault() was called on touchstart and/or touchend.
+                               // 
+                               // This happens on v4.3, 4.2, 4.1, 
+                               // older versions strangely work correctly, 
+                               // but just in case we add delay on all of them)        
+                               var tapDelay = framework.features.isOldAndroid ? 600 : 30;
+                               _blockControlsTapTimeout = setTimeout(function() {
+                                       _blockControlsTap = false;
+                               }, tapDelay);
+                       }
+
+               },
+               _fitControlsInViewport = function() {
+                       return !pswp.likelyTouchDevice || _options.mouseUsed || screen.width > _options.fitControlsWidth;
+               },
+               _togglePswpClass = function(el, cName, add) {
+                       framework[ (add ? 'add' : 'remove') + 'Class' ](el, 'pswp__' + cName);
+               },
+
+               // add class when there is just one item in the gallery
+               // (by default it hides left/right arrows and 1ofX counter)
+               _countNumItems = function() {
+                       var hasOneSlide = (_options.getNumItemsFn() === 1);
+
+                       if(hasOneSlide !== _galleryHasOneSlide) {
+                               _togglePswpClass(_controls, 'ui--one-slide', hasOneSlide);
+                               _galleryHasOneSlide = hasOneSlide;
+                       }
+               },
+               _toggleShareModalClass = function() {
+                       _togglePswpClass(_shareModal, 'share-modal--hidden', _shareModalHidden);
+               },
+               _toggleShareModal = function() {
+
+                       _shareModalHidden = !_shareModalHidden;
+                       
+                       
+                       if(!_shareModalHidden) {
+                               _toggleShareModalClass();
+                               setTimeout(function() {
+                                       if(!_shareModalHidden) {
+                                               framework.addClass(_shareModal, 'pswp__share-modal--fade-in');
+                                       }
+                               }, 30);
+                       } else {
+                               framework.removeClass(_shareModal, 'pswp__share-modal--fade-in');
+                               setTimeout(function() {
+                                       if(_shareModalHidden) {
+                                               _toggleShareModalClass();
+                                       }
+                               }, 300);
+                       }
+                       
+                       if(!_shareModalHidden) {
+                               _updateShareURLs();
+                       }
+                       return false;
+               },
+
+               _openWindowPopup = function(e) {
+                       e = e || window.event;
+                       var target = e.target || e.srcElement;
+
+                       pswp.shout('shareLinkClick', e, target);
+
+                       if(!target.href) {
+                               return false;
+                       }
+
+                       if( target.hasAttribute('download') ) {
+                               return true;
+                       }
+
+                       window.open(target.href, 'pswp_share', 'scrollbars=yes,resizable=yes,toolbar=no,'+
+                                                                               'location=yes,width=550,height=420,top=100,left=' + 
+                                                                               (window.screen ? Math.round(screen.width / 2 - 275) : 100)  );
+
+                       if(!_shareModalHidden) {
+                               _toggleShareModal();
+                       }
+                       
+                       return false;
+               },
+               _updateShareURLs = function() {
+                       var shareButtonOut = '',
+                               shareButtonData,
+                               shareURL,
+                               image_url,
+                               page_url,
+                               share_text;
+
+                       for(var i = 0; i < _options.shareButtons.length; i++) {
+                               shareButtonData = _options.shareButtons[i];
+
+                               image_url = _options.getImageURLForShare(shareButtonData);
+                               page_url = _options.getPageURLForShare(shareButtonData);
+                               share_text = _options.getTextForShare(shareButtonData);
+
+                               shareURL = shareButtonData.url.replace('{{url}}', encodeURIComponent(page_url) )
+                                                                       .replace('{{image_url}}', encodeURIComponent(image_url) )
+                                                                       .replace('{{raw_image_url}}', image_url )
+                                                                       .replace('{{text}}', encodeURIComponent(share_text) );
+
+                               shareButtonOut += '<a href="' + shareURL + '" target="_blank" '+
+                                                                       'class="pswp__share--' + shareButtonData.id + '"' +
+                                                                       (shareButtonData.download ? 'download' : '') + '>' + 
+                                                                       shareButtonData.label + '</a>';
+
+                               if(_options.parseShareButtonOut) {
+                                       shareButtonOut = _options.parseShareButtonOut(shareButtonData, shareButtonOut);
+                               }
+                       }
+                       _shareModal.children[0].innerHTML = shareButtonOut;
+                       _shareModal.children[0].onclick = _openWindowPopup;
+
+               },
+               _hasCloseClass = function(target) {
+                       for(var  i = 0; i < _options.closeElClasses.length; i++) {
+                               if( framework.hasClass(target, 'pswp__' + _options.closeElClasses[i]) ) {
+                                       return true;
+                               }
+                       }
+               },
+               _idleInterval,
+               _idleTimer,
+               _idleIncrement = 0,
+               _onIdleMouseMove = function() {
+                       clearTimeout(_idleTimer);
+                       _idleIncrement = 0;
+                       if(_isIdle) {
+                               ui.setIdle(false);
+                       }
+               },
+               _onMouseLeaveWindow = function(e) {
+                       e = e ? e : window.event;
+                       var from = e.relatedTarget || e.toElement;
+                       if (!from || from.nodeName === 'HTML') {
+                               clearTimeout(_idleTimer);
+                               _idleTimer = setTimeout(function() {
+                                       ui.setIdle(true);
+                               }, _options.timeToIdleOutside);
+                       }
+               },
+               _setupFullscreenAPI = function() {
+                       if(_options.fullscreenEl && !framework.features.isOldAndroid) {
+                               if(!_fullscrenAPI) {
+                                       _fullscrenAPI = ui.getFullscreenAPI();
+                               }
+                               if(_fullscrenAPI) {
+                                       framework.bind(document, _fullscrenAPI.eventK, ui.updateFullscreen);
+                                       ui.updateFullscreen();
+                                       framework.addClass(pswp.template, 'pswp--supports-fs');
+                               } else {
+                                       framework.removeClass(pswp.template, 'pswp--supports-fs');
+                               }
+                       }
+               },
+               _setupLoadingIndicator = function() {
+                       // Setup loading indicator
+                       if(_options.preloaderEl) {
+                       
+                               _toggleLoadingIndicator(true);
+
+                               _listen('beforeChange', function() {
+
+                                       clearTimeout(_loadingIndicatorTimeout);
+
+                                       // display loading indicator with delay
+                                       _loadingIndicatorTimeout = setTimeout(function() {
+
+                                               if(pswp.currItem && pswp.currItem.loading) {
+
+                                                       if( !pswp.allowProgressiveImg() || (pswp.currItem.img && !pswp.currItem.img.naturalWidth)  ) {
+                                                               // show preloader if progressive loading is not enabled, 
+                                                               // or image width is not defined yet (because of slow connection)
+                                                               _toggleLoadingIndicator(false); 
+                                                               // items-controller.js function allowProgressiveImg
+                                                       }
+                                                       
+                                               } else {
+                                                       _toggleLoadingIndicator(true); // hide preloader
+                                               }
+
+                                       }, _options.loadingIndicatorDelay);
+                                       
+                               });
+                               _listen('imageLoadComplete', function(index, item) {
+                                       if(pswp.currItem === item) {
+                                               _toggleLoadingIndicator(true);
+                                       }
+                               });
+
+                       }
+               },
+               _toggleLoadingIndicator = function(hide) {
+                       if( _loadingIndicatorHidden !== hide ) {
+                               _togglePswpClass(_loadingIndicator, 'preloader--active', !hide);
+                               _loadingIndicatorHidden = hide;
+                       }
+               },
+               _applyNavBarGaps = function(item) {
+                       var gap = item.vGap;
+
+                       if( _fitControlsInViewport() ) {
+                               
+                               var bars = _options.barsSize; 
+                               if(_options.captionEl && bars.bottom === 'auto') {
+                                       if(!_fakeCaptionContainer) {
+                                               _fakeCaptionContainer = framework.createEl('pswp__caption pswp__caption--fake');
+                                               _fakeCaptionContainer.appendChild( framework.createEl('pswp__caption__center') );
+                                               _controls.insertBefore(_fakeCaptionContainer, _captionContainer);
+                                               framework.addClass(_controls, 'pswp__ui--fit');
+                                       }
+                                       if( _options.addCaptionHTMLFn(item, _fakeCaptionContainer, true) ) {
+
+                                               var captionSize = _fakeCaptionContainer.clientHeight;
+                                               gap.bottom = parseInt(captionSize,10) || 44;
+                                       } else {
+                                               gap.bottom = bars.top; // if no caption, set size of bottom gap to size of top
+                                       }
+                               } else {
+                                       gap.bottom = bars.bottom === 'auto' ? 0 : bars.bottom;
+                               }
+                               
+                               // height of top bar is static, no need to calculate it
+                               gap.top = bars.top;
+                       } else {
+                               gap.top = gap.bottom = 0;
+                       }
+               },
+               _setupIdle = function() {
+                       // Hide controls when mouse is used
+                       if(_options.timeToIdle) {
+                               _listen('mouseUsed', function() {
+                                       
+                                       framework.bind(document, 'mousemove', _onIdleMouseMove);
+                                       framework.bind(document, 'mouseout', _onMouseLeaveWindow);
+
+                                       _idleInterval = setInterval(function() {
+                                               _idleIncrement++;
+                                               if(_idleIncrement === 2) {
+                                                       ui.setIdle(true);
+                                               }
+                                       }, _options.timeToIdle / 2);
+                               });
+                       }
+               },
+               _setupHidingControlsDuringGestures = function() {
+
+                       // Hide controls on vertical drag
+                       _listen('onVerticalDrag', function(now) {
+                               if(_controlsVisible && now < 0.95) {
+                                       ui.hideControls();
+                               } else if(!_controlsVisible && now >= 0.95) {
+                                       ui.showControls();
+                               }
+                       });
+
+                       // Hide controls when pinching to close
+                       var pinchControlsHidden;
+                       _listen('onPinchClose' , function(now) {
+                               if(_controlsVisible && now < 0.9) {
+                                       ui.hideControls();
+                                       pinchControlsHidden = true;
+                               } else if(pinchControlsHidden && !_controlsVisible && now > 0.9) {
+                                       ui.showControls();
+                               }
+                       });
+
+                       _listen('zoomGestureEnded', function() {
+                               pinchControlsHidden = false;
+                               if(pinchControlsHidden && !_controlsVisible) {
+                                       ui.showControls();
+                               }
+                       });
+
+               };
+
+
+
+       var _uiElements = [
+               { 
+                       name: 'caption', 
+                       option: 'captionEl',
+                       onInit: function(el) {  
+                               _captionContainer = el; 
+                       } 
+               },
+               { 
+                       name: 'share-modal', 
+                       option: 'shareEl',
+                       onInit: function(el) {  
+                               _shareModal = el;
+                       },
+                       onTap: function() {
+                               _toggleShareModal();
+                       } 
+               },
+               { 
+                       name: 'button--share', 
+                       option: 'shareEl',
+                       onInit: function(el) { 
+                               _shareButton = el;
+                       },
+                       onTap: function() {
+                               _toggleShareModal();
+                       } 
+               },
+               { 
+                       name: 'button--zoom', 
+                       option: 'zoomEl',
+                       onTap: pswp.toggleDesktopZoom
+               },
+               { 
+                       name: 'counter', 
+                       option: 'counterEl',
+                       onInit: function(el) {  
+                               _indexIndicator = el;
+                       } 
+               },
+               { 
+                       name: 'button--close', 
+                       option: 'closeEl',
+                       onTap: pswp.close
+               },
+               { 
+                       name: 'button--arrow--left', 
+                       option: 'arrowEl',
+                       onTap: pswp.prev
+               },
+               { 
+                       name: 'button--arrow--right', 
+                       option: 'arrowEl',
+                       onTap: pswp.next
+               },
+               { 
+                       name: 'button--fs', 
+                       option: 'fullscreenEl',
+                       onTap: function() {  
+                               if(_fullscrenAPI.isFullscreen()) {
+                                       _fullscrenAPI.exit();
+                               } else {
+                                       _fullscrenAPI.enter();
+                               }
+                       } 
+               },
+               { 
+                       name: 'preloader', 
+                       option: 'preloaderEl',
+                       onInit: function(el) {  
+                               _loadingIndicator = el;
+                       } 
+               }
+
+       ];
+
+       var _setupUIElements = function() {
+               var item,
+                       classAttr,
+                       uiElement;
+
+               var loopThroughChildElements = function(sChildren) {
+                       if(!sChildren) {
+                               return;
+                       }
+
+                       var l = sChildren.length;
+                       for(var i = 0; i < l; i++) {
+                               item = sChildren[i];
+                               classAttr = item.className;
+
+                               for(var a = 0; a < _uiElements.length; a++) {
+                                       uiElement = _uiElements[a];
+
+                                       if(classAttr.indexOf('pswp__' + uiElement.name) > -1  ) {
+
+                                               if( _options[uiElement.option] ) { // if element is not disabled from options
+                                                       
+                                                       framework.removeClass(item, 'pswp__element--disabled');
+                                                       if(uiElement.onInit) {
+                                                               uiElement.onInit(item);
+                                                       }
+                                                       
+                                                       //item.style.display = 'block';
+                                               } else {
+                                                       framework.addClass(item, 'pswp__element--disabled');
+                                                       //item.style.display = 'none';
+                                               }
+                                       }
+                               }
+                       }
+               };
+               loopThroughChildElements(_controls.children);
+
+               var topBar =  framework.getChildByClass(_controls, 'pswp__top-bar');
+               if(topBar) {
+                       loopThroughChildElements( topBar.children );
+               }
+       };
+
+
+       
+
+       ui.init = function() {
+
+               // extend options
+               framework.extend(pswp.options, _defaultUIOptions, true);
+
+               // create local link for fast access
+               _options = pswp.options;
+
+               // find pswp__ui element
+               _controls = framework.getChildByClass(pswp.scrollWrap, 'pswp__ui');
+
+               // create local link
+               _listen = pswp.listen;
+
+
+               _setupHidingControlsDuringGestures();
+
+               // update controls when slides change
+               _listen('beforeChange', ui.update);
+
+               // toggle zoom on double-tap
+               _listen('doubleTap', function(point) {
+                       var initialZoomLevel = pswp.currItem.initialZoomLevel;
+                       if(pswp.getZoomLevel() !== initialZoomLevel) {
+                               pswp.zoomTo(initialZoomLevel, point, 333);
+                       } else {
+                               pswp.zoomTo(_options.getDoubleTapZoom(false, pswp.currItem), point, 333);
+                       }
+               });
+
+               // Allow text selection in caption
+               _listen('preventDragEvent', function(e, isDown, preventObj) {
+                       var t = e.target || e.srcElement;
+                       if(
+                               t && 
+                               t.getAttribute('class') && e.type.indexOf('mouse') > -1 && 
+                               ( t.getAttribute('class').indexOf('__caption') > 0 || (/(SMALL|STRONG|EM)/i).test(t.tagName) ) 
+                       ) {
+                               preventObj.prevent = false;
+                       }
+               });
+
+               // bind events for UI
+               _listen('bindEvents', function() {
+                       framework.bind(_controls, 'pswpTap click', _onControlsTap);
+                       framework.bind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap);
+
+                       if(!pswp.likelyTouchDevice) {
+                               framework.bind(pswp.scrollWrap, 'mouseover', ui.onMouseOver);
+                       }
+               });
+
+               // unbind events for UI
+               _listen('unbindEvents', function() {
+                       if(!_shareModalHidden) {
+                               _toggleShareModal();
+                       }
+
+                       if(_idleInterval) {
+                               clearInterval(_idleInterval);
+                       }
+                       framework.unbind(document, 'mouseout', _onMouseLeaveWindow);
+                       framework.unbind(document, 'mousemove', _onIdleMouseMove);
+                       framework.unbind(_controls, 'pswpTap click', _onControlsTap);
+                       framework.unbind(pswp.scrollWrap, 'pswpTap', ui.onGlobalTap);
+                       framework.unbind(pswp.scrollWrap, 'mouseover', ui.onMouseOver);
+
+                       if(_fullscrenAPI) {
+                               framework.unbind(document, _fullscrenAPI.eventK, ui.updateFullscreen);
+                               if(_fullscrenAPI.isFullscreen()) {
+                                       _options.hideAnimationDuration = 0;
+                                       _fullscrenAPI.exit();
+                               }
+                               _fullscrenAPI = null;
+                       }
+               });
+
+
+               // clean up things when gallery is destroyed
+               _listen('destroy', function() {
+                       if(_options.captionEl) {
+                               if(_fakeCaptionContainer) {
+                                       _controls.removeChild(_fakeCaptionContainer);
+                               }
+                               framework.removeClass(_captionContainer, 'pswp__caption--empty');
+                       }
+
+                       if(_shareModal) {
+                               _shareModal.children[0].onclick = null;
+                       }
+                       framework.removeClass(_controls, 'pswp__ui--over-close');
+                       framework.addClass( _controls, 'pswp__ui--hidden');
+                       ui.setIdle(false);
+               });
+               
+
+               if(!_options.showAnimationDuration) {
+                       framework.removeClass( _controls, 'pswp__ui--hidden');
+               }
+               _listen('initialZoomIn', function() {
+                       if(_options.showAnimationDuration) {
+                               framework.removeClass( _controls, 'pswp__ui--hidden');
+                       }
+               });
+               _listen('initialZoomOut', function() {
+                       framework.addClass( _controls, 'pswp__ui--hidden');
+               });
+
+               _listen('parseVerticalMargin', _applyNavBarGaps);
+               
+               _setupUIElements();
+
+               if(_options.shareEl && _shareButton && _shareModal) {
+                       _shareModalHidden = true;
+               }
+
+               _countNumItems();
+
+               _setupIdle();
+
+               _setupFullscreenAPI();
+
+               _setupLoadingIndicator();
+       };
+
+       ui.setIdle = function(isIdle) {
+               _isIdle = isIdle;
+               _togglePswpClass(_controls, 'ui--idle', isIdle);
+       };
+
+       ui.update = function() {
+               // Don't update UI if it's hidden
+               if(_controlsVisible && pswp.currItem) {
+                       
+                       ui.updateIndexIndicator();
+
+                       if(_options.captionEl) {
+                               _options.addCaptionHTMLFn(pswp.currItem, _captionContainer);
+
+                               _togglePswpClass(_captionContainer, 'caption--empty', !pswp.currItem.title);
+                       }
+
+                       _overlayUIUpdated = true;
+
+               } else {
+                       _overlayUIUpdated = false;
+               }
+
+               if(!_shareModalHidden) {
+                       _toggleShareModal();
+               }
+
+               _countNumItems();
+       };
+
+       ui.updateFullscreen = function(e) {
+
+               if(e) {
+                       // some browsers change window scroll position during the fullscreen
+                       // so PhotoSwipe updates it just in case
+                       setTimeout(function() {
+                               pswp.setScrollOffset( 0, framework.getScrollY() );
+                       }, 50);
+               }
+               
+               // toogle pswp--fs class on root element
+               framework[ (_fullscrenAPI.isFullscreen() ? 'add' : 'remove') + 'Class' ](pswp.template, 'pswp--fs');
+       };
+
+       ui.updateIndexIndicator = function() {
+               if(_options.counterEl) {
+                       _indexIndicator.innerHTML = (pswp.getCurrentIndex()+1) + 
+                                                                               _options.indexIndicatorSep + 
+                                                                               _options.getNumItemsFn();
+               }
+       };
+       
+       ui.onGlobalTap = function(e) {
+               e = e || window.event;
+               var target = e.target || e.srcElement;
+
+               if(_blockControlsTap) {
+                       return;
+               }
+
+               if(e.detail && e.detail.pointerType === 'mouse') {
+
+                       // close gallery if clicked outside of the image
+                       if(_hasCloseClass(target)) {
+                               pswp.close();
+                               return;
+                       }
+
+                       if(framework.hasClass(target, 'pswp__img')) {
+                               if(pswp.getZoomLevel() === 1 && pswp.getZoomLevel() <= pswp.currItem.fitRatio) {
+                                       if(_options.clickToCloseNonZoomable) {
+                                               pswp.close();
+                                       }
+                               } else {
+                                       pswp.toggleDesktopZoom(e.detail.releasePoint);
+                               }
+                       }
+                       
+               } else {
+
+                       // tap anywhere (except buttons) to toggle visibility of controls
+                       if(_options.tapToToggleControls) {
+                               if(_controlsVisible) {
+                                       ui.hideControls();
+                               } else {
+                                       ui.showControls();
+                               }
+                       }
+
+                       // tap to close gallery
+                       if(_options.tapToClose && (framework.hasClass(target, 'pswp__img') || _hasCloseClass(target)) ) {
+                               pswp.close();
+                               return;
+                       }
+                       
+               }
+       };
+       ui.onMouseOver = function(e) {
+               e = e || window.event;
+               var target = e.target || e.srcElement;
+
+               // add class when mouse is over an element that should close the gallery
+               _togglePswpClass(_controls, 'ui--over-close', _hasCloseClass(target));
+       };
+
+       ui.hideControls = function() {
+               framework.addClass(_controls,'pswp__ui--hidden');
+               _controlsVisible = false;
+       };
+
+       ui.showControls = function() {
+               _controlsVisible = true;
+               if(!_overlayUIUpdated) {
+                       ui.update();
+               }
+               framework.removeClass(_controls,'pswp__ui--hidden');
+       };
+
+       ui.supportsFullscreen = function() {
+               var d = document;
+               return !!(d.exitFullscreen || d.mozCancelFullScreen || d.webkitExitFullscreen || d.msExitFullscreen);
+       };
+
+       ui.getFullscreenAPI = function() {
+               var dE = document.documentElement,
+                       api,
+                       tF = 'fullscreenchange';
+
+               if (dE.requestFullscreen) {
+                       api = {
+                               enterK: 'requestFullscreen',
+                               exitK: 'exitFullscreen',
+                               elementK: 'fullscreenElement',
+                               eventK: tF
+                       };
+
+               } else if(dE.mozRequestFullScreen ) {
+                       api = {
+                               enterK: 'mozRequestFullScreen',
+                               exitK: 'mozCancelFullScreen',
+                               elementK: 'mozFullScreenElement',
+                               eventK: 'moz' + tF
+                       };
+
+                       
+
+               } else if(dE.webkitRequestFullscreen) {
+                       api = {
+                               enterK: 'webkitRequestFullscreen',
+                               exitK: 'webkitExitFullscreen',
+                               elementK: 'webkitFullscreenElement',
+                               eventK: 'webkit' + tF
+                       };
+
+               } else if(dE.msRequestFullscreen) {
+                       api = {
+                               enterK: 'msRequestFullscreen',
+                               exitK: 'msExitFullscreen',
+                               elementK: 'msFullscreenElement',
+                               eventK: 'MSFullscreenChange'
+                       };
+               }
+
+               if(api) {
+                       api.enter = function() { 
+                               // disable close-on-scroll in fullscreen
+                               _initalCloseOnScrollValue = _options.closeOnScroll; 
+                               _options.closeOnScroll = false; 
+
+                               if(this.enterK === 'webkitRequestFullscreen') {
+                                       pswp.template[this.enterK]( Element.ALLOW_KEYBOARD_INPUT );
+                               } else {
+                                       return pswp.template[this.enterK](); 
+                               }
+                       };
+                       api.exit = function() { 
+                               _options.closeOnScroll = _initalCloseOnScrollValue;
+
+                               return document[this.exitK](); 
+
+                       };
+                       api.isFullscreen = function() { return document[this.elementK]; };
+               }
+
+               return api;
+       };
+
+
+
+};
+return PhotoSwipeUI_Default;
+
+
+});
diff --git a/website/js/vendor/photoswipe/photoswipe-ui-default.min.js b/website/js/vendor/photoswipe/photoswipe-ui-default.min.js
new file mode 100644 (file)
index 0000000..ab7bedc
--- /dev/null
@@ -0,0 +1,4 @@
+/*! PhotoSwipe Default UI - 4.1.3 - 2019-01-08
+* http://photoswipe.com
+* Copyright (c) 2019 Dmitry Semenov; */
+!function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.PhotoSwipeUI_Default=b()}(this,function(){"use strict";var a=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v=this,w=!1,x=!0,y=!0,z={barsSize:{top:44,bottom:"auto"},closeElClasses:["item","caption","zoom-wrap","ui","top-bar"],timeToIdle:4e3,timeToIdleOutside:1e3,loadingIndicatorDelay:1e3,addCaptionHTMLFn:function(a,b){return a.title?(b.children[0].innerHTML=a.title,!0):(b.children[0].innerHTML="",!1)},closeEl:!0,captionEl:!0,fullscreenEl:!0,zoomEl:!0,shareEl:!0,counterEl:!0,arrowEl:!0,preloaderEl:!0,tapToClose:!1,tapToToggleControls:!0,clickToCloseNonZoomable:!0,shareButtons:[{id:"facebook",label:"Share on Facebook",url:"https://www.facebook.com/sharer/sharer.php?u={{url}}"},{id:"twitter",label:"Tweet",url:"https://twitter.com/intent/tweet?text={{text}}&url={{url}}"},{id:"pinterest",label:"Pin it",url:"http://www.pinterest.com/pin/create/button/?url={{url}}&media={{image_url}}&description={{text}}"},{id:"download",label:"Download image",url:"{{raw_image_url}}",download:!0}],getImageURLForShare:function(){return a.currItem.src||""},getPageURLForShare:function(){return window.location.href},getTextForShare:function(){return a.currItem.title||""},indexIndicatorSep:" / ",fitControlsWidth:1200},A=function(a){if(r)return!0;a=a||window.event,q.timeToIdle&&q.mouseUsed&&!k&&K();for(var c,d,e=a.target||a.srcElement,f=e.getAttribute("class")||"",g=0;g<S.length;g++)c=S[g],c.onTap&&f.indexOf("pswp__"+c.name)>-1&&(c.onTap(),d=!0);if(d){a.stopPropagation&&a.stopPropagation(),r=!0;var h=b.features.isOldAndroid?600:30;s=setTimeout(function(){r=!1},h)}},B=function(){return!a.likelyTouchDevice||q.mouseUsed||screen.width>q.fitControlsWidth},C=function(a,c,d){b[(d?"add":"remove")+"Class"](a,"pswp__"+c)},D=function(){var a=1===q.getNumItemsFn();a!==p&&(C(d,"ui--one-slide",a),p=a)},E=function(){C(i,"share-modal--hidden",y)},F=function(){return y=!y,y?(b.removeClass(i,"pswp__share-modal--fade-in"),setTimeout(function(){y&&E()},300)):(E(),setTimeout(function(){y||b.addClass(i,"pswp__share-modal--fade-in")},30)),y||H(),!1},G=function(b){b=b||window.event;var c=b.target||b.srcElement;return a.shout("shareLinkClick",b,c),!!c.href&&(!!c.hasAttribute("download")||(window.open(c.href,"pswp_share","scrollbars=yes,resizable=yes,toolbar=no,location=yes,width=550,height=420,top=100,left="+(window.screen?Math.round(screen.width/2-275):100)),y||F(),!1))},H=function(){for(var a,b,c,d,e,f="",g=0;g<q.shareButtons.length;g++)a=q.shareButtons[g],c=q.getImageURLForShare(a),d=q.getPageURLForShare(a),e=q.getTextForShare(a),b=a.url.replace("{{url}}",encodeURIComponent(d)).replace("{{image_url}}",encodeURIComponent(c)).replace("{{raw_image_url}}",c).replace("{{text}}",encodeURIComponent(e)),f+='<a href="'+b+'" target="_blank" class="pswp__share--'+a.id+'"'+(a.download?"download":"")+">"+a.label+"</a>",q.parseShareButtonOut&&(f=q.parseShareButtonOut(a,f));i.children[0].innerHTML=f,i.children[0].onclick=G},I=function(a){for(var c=0;c<q.closeElClasses.length;c++)if(b.hasClass(a,"pswp__"+q.closeElClasses[c]))return!0},J=0,K=function(){clearTimeout(u),J=0,k&&v.setIdle(!1)},L=function(a){a=a?a:window.event;var b=a.relatedTarget||a.toElement;b&&"HTML"!==b.nodeName||(clearTimeout(u),u=setTimeout(function(){v.setIdle(!0)},q.timeToIdleOutside))},M=function(){q.fullscreenEl&&!b.features.isOldAndroid&&(c||(c=v.getFullscreenAPI()),c?(b.bind(document,c.eventK,v.updateFullscreen),v.updateFullscreen(),b.addClass(a.template,"pswp--supports-fs")):b.removeClass(a.template,"pswp--supports-fs"))},N=function(){q.preloaderEl&&(O(!0),l("beforeChange",function(){clearTimeout(o),o=setTimeout(function(){a.currItem&&a.currItem.loading?(!a.allowProgressiveImg()||a.currItem.img&&!a.currItem.img.naturalWidth)&&O(!1):O(!0)},q.loadingIndicatorDelay)}),l("imageLoadComplete",function(b,c){a.currItem===c&&O(!0)}))},O=function(a){n!==a&&(C(m,"preloader--active",!a),n=a)},P=function(a){var c=a.vGap;if(B()){var g=q.barsSize;if(q.captionEl&&"auto"===g.bottom)if(f||(f=b.createEl("pswp__caption pswp__caption--fake"),f.appendChild(b.createEl("pswp__caption__center")),d.insertBefore(f,e),b.addClass(d,"pswp__ui--fit")),q.addCaptionHTMLFn(a,f,!0)){var h=f.clientHeight;c.bottom=parseInt(h,10)||44}else c.bottom=g.top;else c.bottom="auto"===g.bottom?0:g.bottom;c.top=g.top}else c.top=c.bottom=0},Q=function(){q.timeToIdle&&l("mouseUsed",function(){b.bind(document,"mousemove",K),b.bind(document,"mouseout",L),t=setInterval(function(){J++,2===J&&v.setIdle(!0)},q.timeToIdle/2)})},R=function(){l("onVerticalDrag",function(a){x&&a<.95?v.hideControls():!x&&a>=.95&&v.showControls()});var a;l("onPinchClose",function(b){x&&b<.9?(v.hideControls(),a=!0):a&&!x&&b>.9&&v.showControls()}),l("zoomGestureEnded",function(){a=!1,a&&!x&&v.showControls()})},S=[{name:"caption",option:"captionEl",onInit:function(a){e=a}},{name:"share-modal",option:"shareEl",onInit:function(a){i=a},onTap:function(){F()}},{name:"button--share",option:"shareEl",onInit:function(a){h=a},onTap:function(){F()}},{name:"button--zoom",option:"zoomEl",onTap:a.toggleDesktopZoom},{name:"counter",option:"counterEl",onInit:function(a){g=a}},{name:"button--close",option:"closeEl",onTap:a.close},{name:"button--arrow--left",option:"arrowEl",onTap:a.prev},{name:"button--arrow--right",option:"arrowEl",onTap:a.next},{name:"button--fs",option:"fullscreenEl",onTap:function(){c.isFullscreen()?c.exit():c.enter()}},{name:"preloader",option:"preloaderEl",onInit:function(a){m=a}}],T=function(){var a,c,e,f=function(d){if(d)for(var f=d.length,g=0;g<f;g++){a=d[g],c=a.className;for(var h=0;h<S.length;h++)e=S[h],c.indexOf("pswp__"+e.name)>-1&&(q[e.option]?(b.removeClass(a,"pswp__element--disabled"),e.onInit&&e.onInit(a)):b.addClass(a,"pswp__element--disabled"))}};f(d.children);var g=b.getChildByClass(d,"pswp__top-bar");g&&f(g.children)};v.init=function(){b.extend(a.options,z,!0),q=a.options,d=b.getChildByClass(a.scrollWrap,"pswp__ui"),l=a.listen,R(),l("beforeChange",v.update),l("doubleTap",function(b){var c=a.currItem.initialZoomLevel;a.getZoomLevel()!==c?a.zoomTo(c,b,333):a.zoomTo(q.getDoubleTapZoom(!1,a.currItem),b,333)}),l("preventDragEvent",function(a,b,c){var d=a.target||a.srcElement;d&&d.getAttribute("class")&&a.type.indexOf("mouse")>-1&&(d.getAttribute("class").indexOf("__caption")>0||/(SMALL|STRONG|EM)/i.test(d.tagName))&&(c.prevent=!1)}),l("bindEvents",function(){b.bind(d,"pswpTap click",A),b.bind(a.scrollWrap,"pswpTap",v.onGlobalTap),a.likelyTouchDevice||b.bind(a.scrollWrap,"mouseover",v.onMouseOver)}),l("unbindEvents",function(){y||F(),t&&clearInterval(t),b.unbind(document,"mouseout",L),b.unbind(document,"mousemove",K),b.unbind(d,"pswpTap click",A),b.unbind(a.scrollWrap,"pswpTap",v.onGlobalTap),b.unbind(a.scrollWrap,"mouseover",v.onMouseOver),c&&(b.unbind(document,c.eventK,v.updateFullscreen),c.isFullscreen()&&(q.hideAnimationDuration=0,c.exit()),c=null)}),l("destroy",function(){q.captionEl&&(f&&d.removeChild(f),b.removeClass(e,"pswp__caption--empty")),i&&(i.children[0].onclick=null),b.removeClass(d,"pswp__ui--over-close"),b.addClass(d,"pswp__ui--hidden"),v.setIdle(!1)}),q.showAnimationDuration||b.removeClass(d,"pswp__ui--hidden"),l("initialZoomIn",function(){q.showAnimationDuration&&b.removeClass(d,"pswp__ui--hidden")}),l("initialZoomOut",function(){b.addClass(d,"pswp__ui--hidden")}),l("parseVerticalMargin",P),T(),q.shareEl&&h&&i&&(y=!0),D(),Q(),M(),N()},v.setIdle=function(a){k=a,C(d,"ui--idle",a)},v.update=function(){x&&a.currItem?(v.updateIndexIndicator(),q.captionEl&&(q.addCaptionHTMLFn(a.currItem,e),C(e,"caption--empty",!a.currItem.title)),w=!0):w=!1,y||F(),D()},v.updateFullscreen=function(d){d&&setTimeout(function(){a.setScrollOffset(0,b.getScrollY())},50),b[(c.isFullscreen()?"add":"remove")+"Class"](a.template,"pswp--fs")},v.updateIndexIndicator=function(){q.counterEl&&(g.innerHTML=a.getCurrentIndex()+1+q.indexIndicatorSep+q.getNumItemsFn())},v.onGlobalTap=function(c){c=c||window.event;var d=c.target||c.srcElement;if(!r)if(c.detail&&"mouse"===c.detail.pointerType){if(I(d))return void a.close();b.hasClass(d,"pswp__img")&&(1===a.getZoomLevel()&&a.getZoomLevel()<=a.currItem.fitRatio?q.clickToCloseNonZoomable&&a.close():a.toggleDesktopZoom(c.detail.releasePoint))}else if(q.tapToToggleControls&&(x?v.hideControls():v.showControls()),q.tapToClose&&(b.hasClass(d,"pswp__img")||I(d)))return void a.close()},v.onMouseOver=function(a){a=a||window.event;var b=a.target||a.srcElement;C(d,"ui--over-close",I(b))},v.hideControls=function(){b.addClass(d,"pswp__ui--hidden"),x=!1},v.showControls=function(){x=!0,w||v.update(),b.removeClass(d,"pswp__ui--hidden")},v.supportsFullscreen=function(){var a=document;return!!(a.exitFullscreen||a.mozCancelFullScreen||a.webkitExitFullscreen||a.msExitFullscreen)},v.getFullscreenAPI=function(){var b,c=document.documentElement,d="fullscreenchange";return c.requestFullscreen?b={enterK:"requestFullscreen",exitK:"exitFullscreen",elementK:"fullscreenElement",eventK:d}:c.mozRequestFullScreen?b={enterK:"mozRequestFullScreen",exitK:"mozCancelFullScreen",elementK:"mozFullScreenElement",eventK:"moz"+d}:c.webkitRequestFullscreen?b={enterK:"webkitRequestFullscreen",exitK:"webkitExitFullscreen",elementK:"webkitFullscreenElement",eventK:"webkit"+d}:c.msRequestFullscreen&&(b={enterK:"msRequestFullscreen",exitK:"msExitFullscreen",elementK:"msFullscreenElement",eventK:"MSFullscreenChange"}),b&&(b.enter=function(){return j=q.closeOnScroll,q.closeOnScroll=!1,"webkitRequestFullscreen"!==this.enterK?a.template[this.enterK]():void a.template[this.enterK](Element.ALLOW_KEYBOARD_INPUT)},b.exit=function(){return q.closeOnScroll=j,document[this.exitK]()},b.isFullscreen=function(){return document[this.elementK]}),b}};return a});
\ No newline at end of file
diff --git a/website/js/vendor/photoswipe/photoswipe.css b/website/js/vendor/photoswipe/photoswipe.css
new file mode 100644 (file)
index 0000000..0ca0f80
--- /dev/null
@@ -0,0 +1,179 @@
+/*! PhotoSwipe main CSS by Dmitry Semenov | photoswipe.com | MIT license */
+/*
+       Styles for basic PhotoSwipe functionality (sliding area, open/close transitions)
+*/
+/* pswp = photoswipe */
+.pswp {
+  display: none;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  left: 0;
+  top: 0;
+  overflow: hidden;
+  -ms-touch-action: none;
+  touch-action: none;
+  z-index: 1500;
+  -webkit-text-size-adjust: 100%;
+  /* create separate layer, to avoid paint on window.onscroll in webkit/blink */
+  -webkit-backface-visibility: hidden;
+  outline: none; }
+  .pswp * {
+    -webkit-box-sizing: border-box;
+            box-sizing: border-box; }
+  .pswp img {
+    max-width: none; }
+
+/* style is added when JS option showHideOpacity is set to true */
+.pswp--animate_opacity {
+  /* 0.001, because opacity:0 doesn't trigger Paint action, which causes lag at start of transition */
+  opacity: 0.001;
+  will-change: opacity;
+  /* for open/close transition */
+  -webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
+          transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
+
+.pswp--open {
+  display: block; }
+
+.pswp--zoom-allowed .pswp__img {
+  /* autoprefixer: off */
+  cursor: -webkit-zoom-in;
+  cursor: -moz-zoom-in;
+  cursor: zoom-in; }
+
+.pswp--zoomed-in .pswp__img {
+  /* autoprefixer: off */
+  cursor: -webkit-grab;
+  cursor: -moz-grab;
+  cursor: grab; }
+
+.pswp--dragging .pswp__img {
+  /* autoprefixer: off */
+  cursor: -webkit-grabbing;
+  cursor: -moz-grabbing;
+  cursor: grabbing; }
+
+/*
+       Background is added as a separate element.
+       As animating opacity is much faster than animating rgba() background-color.
+*/
+.pswp__bg {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  background: #000;
+  opacity: 0;
+  -webkit-transform: translateZ(0);
+          transform: translateZ(0);
+  -webkit-backface-visibility: hidden;
+  will-change: opacity; }
+
+.pswp__scroll-wrap {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  overflow: hidden; }
+
+.pswp__container,
+.pswp__zoom-wrap {
+  -ms-touch-action: none;
+  touch-action: none;
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0; }
+
+/* Prevent selection and tap highlights */
+.pswp__container,
+.pswp__img {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+      user-select: none;
+  -webkit-tap-highlight-color: transparent;
+  -webkit-touch-callout: none; }
+
+.pswp__zoom-wrap {
+  position: absolute;
+  width: 100%;
+  -webkit-transform-origin: left top;
+  -ms-transform-origin: left top;
+  transform-origin: left top;
+  /* for open/close transition */
+  -webkit-transition: -webkit-transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
+          transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1); }
+
+.pswp__bg {
+  will-change: opacity;
+  /* for open/close transition */
+  -webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
+          transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
+
+.pswp--animated-in .pswp__bg,
+.pswp--animated-in .pswp__zoom-wrap {
+  -webkit-transition: none;
+  transition: none; }
+
+.pswp__container,
+.pswp__zoom-wrap {
+  -webkit-backface-visibility: hidden; }
+
+.pswp__item {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  overflow: hidden; }
+
+.pswp__img {
+  position: absolute;
+  width: auto;
+  height: auto;
+  top: 0;
+  left: 0; }
+
+/*
+       stretched thumbnail or div placeholder element (see below)
+       style is added to avoid flickering in webkit/blink when layers overlap
+*/
+.pswp__img--placeholder {
+  -webkit-backface-visibility: hidden; }
+
+/*
+       div element that matches size of large image
+       large image loads on top of it
+*/
+.pswp__img--placeholder--blank {
+  background: #222; }
+
+.pswp--ie .pswp__img {
+  width: 100% !important;
+  height: auto !important;
+  left: 0;
+  top: 0; }
+
+/*
+       Error message appears when image is not loaded
+       (JS option errorMsg controls markup)
+*/
+.pswp__error-msg {
+  position: absolute;
+  left: 0;
+  top: 50%;
+  width: 100%;
+  text-align: center;
+  font-size: 14px;
+  line-height: 16px;
+  margin-top: -8px;
+  color: #CCC; }
+
+.pswp__error-msg a {
+  color: #CCC;
+  text-decoration: underline; }
diff --git a/website/js/vendor/photoswipe/photoswipe.js b/website/js/vendor/photoswipe/photoswipe.js
new file mode 100644 (file)
index 0000000..467c1ce
--- /dev/null
@@ -0,0 +1,3734 @@
+/*! PhotoSwipe - v4.1.3 - 2019-01-08
+* http://photoswipe.com
+* Copyright (c) 2019 Dmitry Semenov; */
+(function (root, factory) { 
+       if (typeof define === 'function' && define.amd) {
+               define(factory);
+       } else if (typeof exports === 'object') {
+               module.exports = factory();
+       } else {
+               root.PhotoSwipe = factory();
+       }
+})(this, function () {
+
+       'use strict';
+       var PhotoSwipe = function(template, UiClass, items, options){
+
+/*>>framework-bridge*/
+/**
+ *
+ * Set of generic functions used by gallery.
+ * 
+ * You're free to modify anything here as long as functionality is kept.
+ * 
+ */
+var framework = {
+       features: null,
+       bind: function(target, type, listener, unbind) {
+               var methodName = (unbind ? 'remove' : 'add') + 'EventListener';
+               type = type.split(' ');
+               for(var i = 0; i < type.length; i++) {
+                       if(type[i]) {
+                               target[methodName]( type[i], listener, false);
+                       }
+               }
+       },
+       isArray: function(obj) {
+               return (obj instanceof Array);
+       },
+       createEl: function(classes, tag) {
+               var el = document.createElement(tag || 'div');
+               if(classes) {
+                       el.className = classes;
+               }
+               return el;
+       },
+       getScrollY: function() {
+               var yOffset = window.pageYOffset;
+               return yOffset !== undefined ? yOffset : document.documentElement.scrollTop;
+       },
+       unbind: function(target, type, listener) {
+               framework.bind(target,type,listener,true);
+       },
+       removeClass: function(el, className) {
+               var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
+               el.className = el.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 
+       },
+       addClass: function(el, className) {
+               if( !framework.hasClass(el,className) ) {
+                       el.className += (el.className ? ' ' : '') + className;
+               }
+       },
+       hasClass: function(el, className) {
+               return el.className && new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className);
+       },
+       getChildByClass: function(parentEl, childClassName) {
+               var node = parentEl.firstChild;
+               while(node) {
+                       if( framework.hasClass(node, childClassName) ) {
+                               return node;
+                       }
+                       node = node.nextSibling;
+               }
+       },
+       arraySearch: function(array, value, key) {
+               var i = array.length;
+               while(i--) {
+                       if(array[i][key] === value) {
+                               return i;
+                       } 
+               }
+               return -1;
+       },
+       extend: function(o1, o2, preventOverwrite) {
+               for (var prop in o2) {
+                       if (o2.hasOwnProperty(prop)) {
+                               if(preventOverwrite && o1.hasOwnProperty(prop)) {
+                                       continue;
+                               }
+                               o1[prop] = o2[prop];
+                       }
+               }
+       },
+       easing: {
+               sine: {
+                       out: function(k) {
+                               return Math.sin(k * (Math.PI / 2));
+                       },
+                       inOut: function(k) {
+                               return - (Math.cos(Math.PI * k) - 1) / 2;
+                       }
+               },
+               cubic: {
+                       out: function(k) {
+                               return --k * k * k + 1;
+                       }
+               }
+               /*
+                       elastic: {
+                               out: function ( k ) {
+
+                                       var s, a = 0.1, p = 0.4;
+                                       if ( k === 0 ) return 0;
+                                       if ( k === 1 ) return 1;
+                                       if ( !a || a < 1 ) { a = 1; s = p / 4; }
+                                       else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI );
+                                       return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
+
+                               },
+                       },
+                       back: {
+                               out: function ( k ) {
+                                       var s = 1.70158;
+                                       return --k * k * ( ( s + 1 ) * k + s ) + 1;
+                               }
+                       }
+               */
+       },
+
+       /**
+        * 
+        * @return {object}
+        * 
+        * {
+        *  raf : request animation frame function
+        *  caf : cancel animation frame function
+        *  transfrom : transform property key (with vendor), or null if not supported
+        *  oldIE : IE8 or below
+        * }
+        * 
+        */
+       detectFeatures: function() {
+               if(framework.features) {
+                       return framework.features;
+               }
+               var helperEl = framework.createEl(),
+                       helperStyle = helperEl.style,
+                       vendor = '',
+                       features = {};
+
+               // IE8 and below
+               features.oldIE = document.all && !document.addEventListener;
+
+               features.touch = 'ontouchstart' in window;
+
+               if(window.requestAnimationFrame) {
+                       features.raf = window.requestAnimationFrame;
+                       features.caf = window.cancelAnimationFrame;
+               }
+
+               features.pointerEvent = !!(window.PointerEvent) || navigator.msPointerEnabled;
+
+               // fix false-positive detection of old Android in new IE
+               // (IE11 ua string contains "Android 4.0")
+               
+               if(!features.pointerEvent) { 
+
+                       var ua = navigator.userAgent;
+
+                       // Detect if device is iPhone or iPod and if it's older than iOS 8
+                       // http://stackoverflow.com/a/14223920
+                       // 
+                       // This detection is made because of buggy top/bottom toolbars
+                       // that don't trigger window.resize event.
+                       // For more info refer to _isFixedPosition variable in core.js
+
+                       if (/iP(hone|od)/.test(navigator.platform)) {
+                               var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
+                               if(v && v.length > 0) {
+                                       v = parseInt(v[1], 10);
+                                       if(v >= 1 && v < 8 ) {
+                                               features.isOldIOSPhone = true;
+                                       }
+                               }
+                       }
+
+                       // Detect old Android (before KitKat)
+                       // due to bugs related to position:fixed
+                       // http://stackoverflow.com/questions/7184573/pick-up-the-android-version-in-the-browser-by-javascript
+                       
+                       var match = ua.match(/Android\s([0-9\.]*)/);
+                       var androidversion =  match ? match[1] : 0;
+                       androidversion = parseFloat(androidversion);
+                       if(androidversion >= 1 ) {
+                               if(androidversion < 4.4) {
+                                       features.isOldAndroid = true; // for fixed position bug & performance
+                               }
+                               features.androidVersion = androidversion; // for touchend bug
+                       }       
+                       features.isMobileOpera = /opera mini|opera mobi/i.test(ua);
+
+                       // p.s. yes, yes, UA sniffing is bad, propose your solution for above bugs.
+               }
+               
+               var styleChecks = ['transform', 'perspective', 'animationName'],
+                       vendors = ['', 'webkit','Moz','ms','O'],
+                       styleCheckItem,
+                       styleName;
+
+               for(var i = 0; i < 4; i++) {
+                       vendor = vendors[i];
+
+                       for(var a = 0; a < 3; a++) {
+                               styleCheckItem = styleChecks[a];
+
+                               // uppercase first letter of property name, if vendor is present
+                               styleName = vendor + (vendor ? 
+                                                                               styleCheckItem.charAt(0).toUpperCase() + styleCheckItem.slice(1) : 
+                                                                               styleCheckItem);
+                       
+                               if(!features[styleCheckItem] && styleName in helperStyle ) {
+                                       features[styleCheckItem] = styleName;
+                               }
+                       }
+
+                       if(vendor && !features.raf) {
+                               vendor = vendor.toLowerCase();
+                               features.raf = window[vendor+'RequestAnimationFrame'];
+                               if(features.raf) {
+                                       features.caf = window[vendor+'CancelAnimationFrame'] || 
+                                                                       window[vendor+'CancelRequestAnimationFrame'];
+                               }
+                       }
+               }
+                       
+               if(!features.raf) {
+                       var lastTime = 0;
+                       features.raf = function(fn) {
+                               var currTime = new Date().getTime();
+                               var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+                               var id = window.setTimeout(function() { fn(currTime + timeToCall); }, timeToCall);
+                               lastTime = currTime + timeToCall;
+                               return id;
+                       };
+                       features.caf = function(id) { clearTimeout(id); };
+               }
+
+               // Detect SVG support
+               features.svg = !!document.createElementNS && 
+                                               !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect;
+
+               framework.features = features;
+
+               return features;
+       }
+};
+
+framework.detectFeatures();
+
+// Override addEventListener for old versions of IE
+if(framework.features.oldIE) {
+
+       framework.bind = function(target, type, listener, unbind) {
+               
+               type = type.split(' ');
+
+               var methodName = (unbind ? 'detach' : 'attach') + 'Event',
+                       evName,
+                       _handleEv = function() {
+                               listener.handleEvent.call(listener);
+                       };
+
+               for(var i = 0; i < type.length; i++) {
+                       evName = type[i];
+                       if(evName) {
+
+                               if(typeof listener === 'object' && listener.handleEvent) {
+                                       if(!unbind) {
+                                               listener['oldIE' + evName] = _handleEv;
+                                       } else {
+                                               if(!listener['oldIE' + evName]) {
+                                                       return false;
+                                               }
+                                       }
+
+                                       target[methodName]( 'on' + evName, listener['oldIE' + evName]);
+                               } else {
+                                       target[methodName]( 'on' + evName, listener);
+                               }
+
+                       }
+               }
+       };
+       
+}
+
+/*>>framework-bridge*/
+
+/*>>core*/
+//function(template, UiClass, items, options)
+
+var self = this;
+
+/**
+ * Static vars, don't change unless you know what you're doing.
+ */
+var DOUBLE_TAP_RADIUS = 25, 
+       NUM_HOLDERS = 3;
+
+/**
+ * Options
+ */
+var _options = {
+       allowPanToNext:true,
+       spacing: 0.12,
+       bgOpacity: 1,
+       mouseUsed: false,
+       loop: true,
+       pinchToClose: true,
+       closeOnScroll: true,
+       closeOnVerticalDrag: true,
+       verticalDragRange: 0.75,
+       hideAnimationDuration: 333,
+       showAnimationDuration: 333,
+       showHideOpacity: false,
+       focus: true,
+       escKey: true,
+       arrowKeys: true,
+       mainScrollEndFriction: 0.35,
+       panEndFriction: 0.35,
+       isClickableElement: function(el) {
+        return el.tagName === 'A';
+    },
+    getDoubleTapZoom: function(isMouseClick, item) {
+       if(isMouseClick) {
+               return 1;
+       } else {
+               return item.initialZoomLevel < 0.7 ? 1 : 1.33;
+       }
+    },
+    maxSpreadZoom: 1.33,
+       modal: true,
+
+       // not fully implemented yet
+       scaleMode: 'fit' // TODO
+};
+framework.extend(_options, options);
+
+
+/**
+ * Private helper variables & functions
+ */
+
+var _getEmptyPoint = function() { 
+               return {x:0,y:0}; 
+       };
+
+var _isOpen,
+       _isDestroying,
+       _closedByScroll,
+       _currentItemIndex,
+       _containerStyle,
+       _containerShiftIndex,
+       _currPanDist = _getEmptyPoint(),
+       _startPanOffset = _getEmptyPoint(),
+       _panOffset = _getEmptyPoint(),
+       _upMoveEvents, // drag move, drag end & drag cancel events array
+       _downEvents, // drag start events array
+       _globalEventHandlers,
+       _viewportSize = {},
+       _currZoomLevel,
+       _startZoomLevel,
+       _translatePrefix,
+       _translateSufix,
+       _updateSizeInterval,
+       _itemsNeedUpdate,
+       _currPositionIndex = 0,
+       _offset = {},
+       _slideSize = _getEmptyPoint(), // size of slide area, including spacing
+       _itemHolders,
+       _prevItemIndex,
+       _indexDiff = 0, // difference of indexes since last content update
+       _dragStartEvent,
+       _dragMoveEvent,
+       _dragEndEvent,
+       _dragCancelEvent,
+       _transformKey,
+       _pointerEventEnabled,
+       _isFixedPosition = true,
+       _likelyTouchDevice,
+       _modules = [],
+       _requestAF,
+       _cancelAF,
+       _initalClassName,
+       _initalWindowScrollY,
+       _oldIE,
+       _currentWindowScrollY,
+       _features,
+       _windowVisibleSize = {},
+       _renderMaxResolution = false,
+       _orientationChangeTimeout,
+
+
+       // Registers PhotoSWipe module (History, Controller ...)
+       _registerModule = function(name, module) {
+               framework.extend(self, module.publicMethods);
+               _modules.push(name);
+       },
+
+       _getLoopedId = function(index) {
+               var numSlides = _getNumItems();
+               if(index > numSlides - 1) {
+                       return index - numSlides;
+               } else  if(index < 0) {
+                       return numSlides + index;
+               }
+               return index;
+       },
+       
+       // Micro bind/trigger
+       _listeners = {},
+       _listen = function(name, fn) {
+               if(!_listeners[name]) {
+                       _listeners[name] = [];
+               }
+               return _listeners[name].push(fn);
+       },
+       _shout = function(name) {
+               var listeners = _listeners[name];
+
+               if(listeners) {
+                       var args = Array.prototype.slice.call(arguments);
+                       args.shift();
+
+                       for(var i = 0; i < listeners.length; i++) {
+                               listeners[i].apply(self, args);
+                       }
+               }
+       },
+
+       _getCurrentTime = function() {
+               return new Date().getTime();
+       },
+       _applyBgOpacity = function(opacity) {
+               _bgOpacity = opacity;
+               self.bg.style.opacity = opacity * _options.bgOpacity;
+       },
+
+       _applyZoomTransform = function(styleObj,x,y,zoom,item) {
+               if(!_renderMaxResolution || (item && item !== self.currItem) ) {
+                       zoom = zoom / (item ? item.fitRatio : self.currItem.fitRatio);  
+               }
+                       
+               styleObj[_transformKey] = _translatePrefix + x + 'px, ' + y + 'px' + _translateSufix + ' scale(' + zoom + ')';
+       },
+       _applyCurrentZoomPan = function( allowRenderResolution ) {
+               if(_currZoomElementStyle) {
+
+                       if(allowRenderResolution) {
+                               if(_currZoomLevel > self.currItem.fitRatio) {
+                                       if(!_renderMaxResolution) {
+                                               _setImageSize(self.currItem, false, true);
+                                               _renderMaxResolution = true;
+                                       }
+                               } else {
+                                       if(_renderMaxResolution) {
+                                               _setImageSize(self.currItem);
+                                               _renderMaxResolution = false;
+                                       }
+                               }
+                       }
+                       
+
+                       _applyZoomTransform(_currZoomElementStyle, _panOffset.x, _panOffset.y, _currZoomLevel);
+               }
+       },
+       _applyZoomPanToItem = function(item) {
+               if(item.container) {
+
+                       _applyZoomTransform(item.container.style, 
+                                                               item.initialPosition.x, 
+                                                               item.initialPosition.y, 
+                                                               item.initialZoomLevel,
+                                                               item);
+               }
+       },
+       _setTranslateX = function(x, elStyle) {
+               elStyle[_transformKey] = _translatePrefix + x + 'px, 0px' + _translateSufix;
+       },
+       _moveMainScroll = function(x, dragging) {
+
+               if(!_options.loop && dragging) {
+                       var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x) / _slideSize.x,
+                               delta = Math.round(x - _mainScrollPos.x);
+
+                       if( (newSlideIndexOffset < 0 && delta > 0) || 
+                               (newSlideIndexOffset >= _getNumItems() - 1 && delta < 0) ) {
+                               x = _mainScrollPos.x + delta * _options.mainScrollEndFriction;
+                       } 
+               }
+               
+               _mainScrollPos.x = x;
+               _setTranslateX(x, _containerStyle);
+       },
+       _calculatePanOffset = function(axis, zoomLevel) {
+               var m = _midZoomPoint[axis] - _offset[axis];
+               return _startPanOffset[axis] + _currPanDist[axis] + m - m * ( zoomLevel / _startZoomLevel );
+       },
+       
+       _equalizePoints = function(p1, p2) {
+               p1.x = p2.x;
+               p1.y = p2.y;
+               if(p2.id) {
+                       p1.id = p2.id;
+               }
+       },
+       _roundPoint = function(p) {
+               p.x = Math.round(p.x);
+               p.y = Math.round(p.y);
+       },
+
+       _mouseMoveTimeout = null,
+       _onFirstMouseMove = function() {
+               // Wait until mouse move event is fired at least twice during 100ms
+               // We do this, because some mobile browsers trigger it on touchstart
+               if(_mouseMoveTimeout ) { 
+                       framework.unbind(document, 'mousemove', _onFirstMouseMove);
+                       framework.addClass(template, 'pswp--has_mouse');
+                       _options.mouseUsed = true;
+                       _shout('mouseUsed');
+               }
+               _mouseMoveTimeout = setTimeout(function() {
+                       _mouseMoveTimeout = null;
+               }, 100);
+       },
+
+       _bindEvents = function() {
+               framework.bind(document, 'keydown', self);
+
+               if(_features.transform) {
+                       // don't bind click event in browsers that don't support transform (mostly IE8)
+                       framework.bind(self.scrollWrap, 'click', self);
+               }
+               
+
+               if(!_options.mouseUsed) {
+                       framework.bind(document, 'mousemove', _onFirstMouseMove);
+               }
+
+               framework.bind(window, 'resize scroll orientationchange', self);
+
+               _shout('bindEvents');
+       },
+
+       _unbindEvents = function() {
+               framework.unbind(window, 'resize scroll orientationchange', self);
+               framework.unbind(window, 'scroll', _globalEventHandlers.scroll);
+               framework.unbind(document, 'keydown', self);
+               framework.unbind(document, 'mousemove', _onFirstMouseMove);
+
+               if(_features.transform) {
+                       framework.unbind(self.scrollWrap, 'click', self);
+               }
+
+               if(_isDragging) {
+                       framework.unbind(window, _upMoveEvents, self);
+               }
+
+               clearTimeout(_orientationChangeTimeout);
+
+               _shout('unbindEvents');
+       },
+       
+       _calculatePanBounds = function(zoomLevel, update) {
+               var bounds = _calculateItemSize( self.currItem, _viewportSize, zoomLevel );
+               if(update) {
+                       _currPanBounds = bounds;
+               }
+               return bounds;
+       },
+       
+       _getMinZoomLevel = function(item) {
+               if(!item) {
+                       item = self.currItem;
+               }
+               return item.initialZoomLevel;
+       },
+       _getMaxZoomLevel = function(item) {
+               if(!item) {
+                       item = self.currItem;
+               }
+               return item.w > 0 ? _options.maxSpreadZoom : 1;
+       },
+
+       // Return true if offset is out of the bounds
+       _modifyDestPanOffset = function(axis, destPanBounds, destPanOffset, destZoomLevel) {
+               if(destZoomLevel === self.currItem.initialZoomLevel) {
+                       destPanOffset[axis] = self.currItem.initialPosition[axis];
+                       return true;
+               } else {
+                       destPanOffset[axis] = _calculatePanOffset(axis, destZoomLevel); 
+
+                       if(destPanOffset[axis] > destPanBounds.min[axis]) {
+                               destPanOffset[axis] = destPanBounds.min[axis];
+                               return true;
+                       } else if(destPanOffset[axis] < destPanBounds.max[axis] ) {
+                               destPanOffset[axis] = destPanBounds.max[axis];
+                               return true;
+                       }
+               }
+               return false;
+       },
+
+       _setupTransforms = function() {
+
+               if(_transformKey) {
+                       // setup 3d transforms
+                       var allow3dTransform = _features.perspective && !_likelyTouchDevice;
+                       _translatePrefix = 'translate' + (allow3dTransform ? '3d(' : '(');
+                       _translateSufix = _features.perspective ? ', 0px)' : ')';       
+                       return;
+               }
+
+               // Override zoom/pan/move functions in case old browser is used (most likely IE)
+               // (so they use left/top/width/height, instead of CSS transform)
+       
+               _transformKey = 'left';
+               framework.addClass(template, 'pswp--ie');
+
+               _setTranslateX = function(x, elStyle) {
+                       elStyle.left = x + 'px';
+               };
+               _applyZoomPanToItem = function(item) {
+
+                       var zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
+                               s = item.container.style,
+                               w = zoomRatio * item.w,
+                               h = zoomRatio * item.h;
+
+                       s.width = w + 'px';
+                       s.height = h + 'px';
+                       s.left = item.initialPosition.x + 'px';
+                       s.top = item.initialPosition.y + 'px';
+
+               };
+               _applyCurrentZoomPan = function() {
+                       if(_currZoomElementStyle) {
+
+                               var s = _currZoomElementStyle,
+                                       item = self.currItem,
+                                       zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio,
+                                       w = zoomRatio * item.w,
+                                       h = zoomRatio * item.h;
+
+                               s.width = w + 'px';
+                               s.height = h + 'px';
+
+
+                               s.left = _panOffset.x + 'px';
+                               s.top = _panOffset.y + 'px';
+                       }
+                       
+               };
+       },
+
+       _onKeyDown = function(e) {
+               var keydownAction = '';
+               if(_options.escKey && e.keyCode === 27) { 
+                       keydownAction = 'close';
+               } else if(_options.arrowKeys) {
+                       if(e.keyCode === 37) {
+                               keydownAction = 'prev';
+                       } else if(e.keyCode === 39) { 
+                               keydownAction = 'next';
+                       }
+               }
+
+               if(keydownAction) {
+                       // don't do anything if special key pressed to prevent from overriding default browser actions
+                       // e.g. in Chrome on Mac cmd+arrow-left returns to previous page
+                       if( !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey ) {
+                               if(e.preventDefault) {
+                                       e.preventDefault();
+                               } else {
+                                       e.returnValue = false;
+                               } 
+                               self[keydownAction]();
+                       }
+               }
+       },
+
+       _onGlobalClick = function(e) {
+               if(!e) {
+                       return;
+               }
+
+               // don't allow click event to pass through when triggering after drag or some other gesture
+               if(_moved || _zoomStarted || _mainScrollAnimating || _verticalDragInitiated) {
+                       e.preventDefault();
+                       e.stopPropagation();
+               }
+       },
+
+       _updatePageScrollOffset = function() {
+               self.setScrollOffset(0, framework.getScrollY());                
+       };
+       
+
+
+       
+
+
+
+// Micro animation engine
+var _animations = {},
+       _numAnimations = 0,
+       _stopAnimation = function(name) {
+               if(_animations[name]) {
+                       if(_animations[name].raf) {
+                               _cancelAF( _animations[name].raf );
+                       }
+                       _numAnimations--;
+                       delete _animations[name];
+               }
+       },
+       _registerStartAnimation = function(name) {
+               if(_animations[name]) {
+                       _stopAnimation(name);
+               }
+               if(!_animations[name]) {
+                       _numAnimations++;
+                       _animations[name] = {};
+               }
+       },
+       _stopAllAnimations = function() {
+               for (var prop in _animations) {
+
+                       if( _animations.hasOwnProperty( prop ) ) {
+                               _stopAnimation(prop);
+                       } 
+                       
+               }
+       },
+       _animateProp = function(name, b, endProp, d, easingFn, onUpdate, onComplete) {
+               var startAnimTime = _getCurrentTime(), t;
+               _registerStartAnimation(name);
+
+               var animloop = function(){
+                       if ( _animations[name] ) {
+                               
+                               t = _getCurrentTime() - startAnimTime; // time diff
+                               //b - beginning (start prop)
+                               //d - anim duration
+
+                               if ( t >= d ) {
+                                       _stopAnimation(name);
+                                       onUpdate(endProp);
+                                       if(onComplete) {
+                                               onComplete();
+                                       }
+                                       return;
+                               }
+                               onUpdate( (endProp - b) * easingFn(t/d) + b );
+
+                               _animations[name].raf = _requestAF(animloop);
+                       }
+               };
+               animloop();
+       };
+       
+
+
+var publicMethods = {
+
+       // make a few local variables and functions public
+       shout: _shout,
+       listen: _listen,
+       viewportSize: _viewportSize,
+       options: _options,
+
+       isMainScrollAnimating: function() {
+               return _mainScrollAnimating;
+       },
+       getZoomLevel: function() {
+               return _currZoomLevel;
+       },
+       getCurrentIndex: function() {
+               return _currentItemIndex;
+       },
+       isDragging: function() {
+               return _isDragging;
+       },      
+       isZooming: function() {
+               return _isZooming;
+       },
+       setScrollOffset: function(x,y) {
+               _offset.x = x;
+               _currentWindowScrollY = _offset.y = y;
+               _shout('updateScrollOffset', _offset);
+       },
+       applyZoomPan: function(zoomLevel,panX,panY,allowRenderResolution) {
+               _panOffset.x = panX;
+               _panOffset.y = panY;
+               _currZoomLevel = zoomLevel;
+               _applyCurrentZoomPan( allowRenderResolution );
+       },
+
+       init: function() {
+
+               if(_isOpen || _isDestroying) {
+                       return;
+               }
+
+               var i;
+
+               self.framework = framework; // basic functionality
+               self.template = template; // root DOM element of PhotoSwipe
+               self.bg = framework.getChildByClass(template, 'pswp__bg');
+
+               _initalClassName = template.className;
+               _isOpen = true;
+                               
+               _features = framework.detectFeatures();
+               _requestAF = _features.raf;
+               _cancelAF = _features.caf;
+               _transformKey = _features.transform;
+               _oldIE = _features.oldIE;
+               
+               self.scrollWrap = framework.getChildByClass(template, 'pswp__scroll-wrap');
+               self.container = framework.getChildByClass(self.scrollWrap, 'pswp__container');
+
+               _containerStyle = self.container.style; // for fast access
+
+               // Objects that hold slides (there are only 3 in DOM)
+               self.itemHolders = _itemHolders = [
+                       {el:self.container.children[0] , wrap:0, index: -1},
+                       {el:self.container.children[1] , wrap:0, index: -1},
+                       {el:self.container.children[2] , wrap:0, index: -1}
+               ];
+
+               // hide nearby item holders until initial zoom animation finishes (to avoid extra Paints)
+               _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'none';
+
+               _setupTransforms();
+
+               // Setup global events
+               _globalEventHandlers = {
+                       resize: self.updateSize,
+
+                       // Fixes: iOS 10.3 resize event
+                       // does not update scrollWrap.clientWidth instantly after resize
+                       // https://github.com/dimsemenov/PhotoSwipe/issues/1315
+                       orientationchange: function() {
+                               clearTimeout(_orientationChangeTimeout);
+                               _orientationChangeTimeout = setTimeout(function() {
+                                       if(_viewportSize.x !== self.scrollWrap.clientWidth) {
+                                               self.updateSize();
+                                       }
+                               }, 500);
+                       },
+                       scroll: _updatePageScrollOffset,
+                       keydown: _onKeyDown,
+                       click: _onGlobalClick
+               };
+
+               // disable show/hide effects on old browsers that don't support CSS animations or transforms, 
+               // old IOS, Android and Opera mobile. Blackberry seems to work fine, even older models.
+               var oldPhone = _features.isOldIOSPhone || _features.isOldAndroid || _features.isMobileOpera;
+               if(!_features.animationName || !_features.transform || oldPhone) {
+                       _options.showAnimationDuration = _options.hideAnimationDuration = 0;
+               }
+
+               // init modules
+               for(i = 0; i < _modules.length; i++) {
+                       self['init' + _modules[i]]();
+               }
+               
+               // init
+               if(UiClass) {
+                       var ui = self.ui = new UiClass(self, framework);
+                       ui.init();
+               }
+
+               _shout('firstUpdate');
+               _currentItemIndex = _currentItemIndex || _options.index || 0;
+               // validate index
+               if( isNaN(_currentItemIndex) || _currentItemIndex < 0 || _currentItemIndex >= _getNumItems() ) {
+                       _currentItemIndex = 0;
+               }
+               self.currItem = _getItemAt( _currentItemIndex );
+
+               
+               if(_features.isOldIOSPhone || _features.isOldAndroid) {
+                       _isFixedPosition = false;
+               }
+               
+               template.setAttribute('aria-hidden', 'false');
+               if(_options.modal) {
+                       if(!_isFixedPosition) {
+                               template.style.position = 'absolute';
+                               template.style.top = framework.getScrollY() + 'px';
+                       } else {
+                               template.style.position = 'fixed';
+                       }
+               }
+
+               if(_currentWindowScrollY === undefined) {
+                       _shout('initialLayout');
+                       _currentWindowScrollY = _initalWindowScrollY = framework.getScrollY();
+               }
+               
+               // add classes to root element of PhotoSwipe
+               var rootClasses = 'pswp--open ';
+               if(_options.mainClass) {
+                       rootClasses += _options.mainClass + ' ';
+               }
+               if(_options.showHideOpacity) {
+                       rootClasses += 'pswp--animate_opacity ';
+               }
+               rootClasses += _likelyTouchDevice ? 'pswp--touch' : 'pswp--notouch';
+               rootClasses += _features.animationName ? ' pswp--css_animation' : '';
+               rootClasses += _features.svg ? ' pswp--svg' : '';
+               framework.addClass(template, rootClasses);
+
+               self.updateSize();
+
+               // initial update
+               _containerShiftIndex = -1;
+               _indexDiff = null;
+               for(i = 0; i < NUM_HOLDERS; i++) {
+                       _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, _itemHolders[i].el.style);
+               }
+
+               if(!_oldIE) {
+                       framework.bind(self.scrollWrap, _downEvents, self); // no dragging for old IE
+               }       
+
+               _listen('initialZoomInEnd', function() {
+                       self.setContent(_itemHolders[0], _currentItemIndex-1);
+                       self.setContent(_itemHolders[2], _currentItemIndex+1);
+
+                       _itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'block';
+
+                       if(_options.focus) {
+                               // focus causes layout, 
+                               // which causes lag during the animation, 
+                               // that's why we delay it untill the initial zoom transition ends
+                               template.focus();
+                       }
+                        
+
+                       _bindEvents();
+               });
+
+               // set content for center slide (first time)
+               self.setContent(_itemHolders[1], _currentItemIndex);
+               
+               self.updateCurrItem();
+
+               _shout('afterInit');
+
+               if(!_isFixedPosition) {
+
+                       // On all versions of iOS lower than 8.0, we check size of viewport every second.
+                       // 
+                       // This is done to detect when Safari top & bottom bars appear, 
+                       // as this action doesn't trigger any events (like resize). 
+                       // 
+                       // On iOS8 they fixed this.
+                       // 
+                       // 10 Nov 2014: iOS 7 usage ~40%. iOS 8 usage 56%.
+                       
+                       _updateSizeInterval = setInterval(function() {
+                               if(!_numAnimations && !_isDragging && !_isZooming && (_currZoomLevel === self.currItem.initialZoomLevel)  ) {
+                                       self.updateSize();
+                               }
+                       }, 1000);
+               }
+
+               framework.addClass(template, 'pswp--visible');
+       },
+
+       // Close the gallery, then destroy it
+       close: function() {
+               if(!_isOpen) {
+                       return;
+               }
+
+               _isOpen = false;
+               _isDestroying = true;
+               _shout('close');
+               _unbindEvents();
+
+               _showOrHide(self.currItem, null, true, self.destroy);
+       },
+
+       // destroys the gallery (unbinds events, cleans up intervals and timeouts to avoid memory leaks)
+       destroy: function() {
+               _shout('destroy');
+
+               if(_showOrHideTimeout) {
+                       clearTimeout(_showOrHideTimeout);
+               }
+               
+               template.setAttribute('aria-hidden', 'true');
+               template.className = _initalClassName;
+
+               if(_updateSizeInterval) {
+                       clearInterval(_updateSizeInterval);
+               }
+
+               framework.unbind(self.scrollWrap, _downEvents, self);
+
+               // we unbind scroll event at the end, as closing animation may depend on it
+               framework.unbind(window, 'scroll', self);
+
+               _stopDragUpdateLoop();
+
+               _stopAllAnimations();
+
+               _listeners = null;
+       },
+
+       /**
+        * Pan image to position
+        * @param {Number} x     
+        * @param {Number} y     
+        * @param {Boolean} force Will ignore bounds if set to true.
+        */
+       panTo: function(x,y,force) {
+               if(!force) {
+                       if(x > _currPanBounds.min.x) {
+                               x = _currPanBounds.min.x;
+                       } else if(x < _currPanBounds.max.x) {
+                               x = _currPanBounds.max.x;
+                       }
+
+                       if(y > _currPanBounds.min.y) {
+                               y = _currPanBounds.min.y;
+                       } else if(y < _currPanBounds.max.y) {
+                               y = _currPanBounds.max.y;
+                       }
+               }
+               
+               _panOffset.x = x;
+               _panOffset.y = y;
+               _applyCurrentZoomPan();
+       },
+       
+       handleEvent: function (e) {
+               e = e || window.event;
+               if(_globalEventHandlers[e.type]) {
+                       _globalEventHandlers[e.type](e);
+               }
+       },
+
+
+       goTo: function(index) {
+
+               index = _getLoopedId(index);
+
+               var diff = index - _currentItemIndex;
+               _indexDiff = diff;
+
+               _currentItemIndex = index;
+               self.currItem = _getItemAt( _currentItemIndex );
+               _currPositionIndex -= diff;
+               
+               _moveMainScroll(_slideSize.x * _currPositionIndex);
+               
+
+               _stopAllAnimations();
+               _mainScrollAnimating = false;
+
+               self.updateCurrItem();
+       },
+       next: function() {
+               self.goTo( _currentItemIndex + 1);
+       },
+       prev: function() {
+               self.goTo( _currentItemIndex - 1);
+       },
+
+       // update current zoom/pan objects
+       updateCurrZoomItem: function(emulateSetContent) {
+               if(emulateSetContent) {
+                       _shout('beforeChange', 0);
+               }
+
+               // itemHolder[1] is middle (current) item
+               if(_itemHolders[1].el.children.length) {
+                       var zoomElement = _itemHolders[1].el.children[0];
+                       if( framework.hasClass(zoomElement, 'pswp__zoom-wrap') ) {
+                               _currZoomElementStyle = zoomElement.style;
+                       } else {
+                               _currZoomElementStyle = null;
+                       }
+               } else {
+                       _currZoomElementStyle = null;
+               }
+               
+               _currPanBounds = self.currItem.bounds;  
+               _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
+
+               _panOffset.x = _currPanBounds.center.x;
+               _panOffset.y = _currPanBounds.center.y;
+
+               if(emulateSetContent) {
+                       _shout('afterChange');
+               }
+       },
+
+
+       invalidateCurrItems: function() {
+               _itemsNeedUpdate = true;
+               for(var i = 0; i < NUM_HOLDERS; i++) {
+                       if( _itemHolders[i].item ) {
+                               _itemHolders[i].item.needsUpdate = true;
+                       }
+               }
+       },
+
+       updateCurrItem: function(beforeAnimation) {
+
+               if(_indexDiff === 0) {
+                       return;
+               }
+
+               var diffAbs = Math.abs(_indexDiff),
+                       tempHolder;
+
+               if(beforeAnimation && diffAbs < 2) {
+                       return;
+               }
+
+
+               self.currItem = _getItemAt( _currentItemIndex );
+               _renderMaxResolution = false;
+               
+               _shout('beforeChange', _indexDiff);
+
+               if(diffAbs >= NUM_HOLDERS) {
+                       _containerShiftIndex += _indexDiff + (_indexDiff > 0 ? -NUM_HOLDERS : NUM_HOLDERS);
+                       diffAbs = NUM_HOLDERS;
+               }
+               for(var i = 0; i < diffAbs; i++) {
+                       if(_indexDiff > 0) {
+                               tempHolder = _itemHolders.shift();
+                               _itemHolders[NUM_HOLDERS-1] = tempHolder; // move first to last
+
+                               _containerShiftIndex++;
+                               _setTranslateX( (_containerShiftIndex+2) * _slideSize.x, tempHolder.el.style);
+                               self.setContent(tempHolder, _currentItemIndex - diffAbs + i + 1 + 1);
+                       } else {
+                               tempHolder = _itemHolders.pop();
+                               _itemHolders.unshift( tempHolder ); // move last to first
+
+                               _containerShiftIndex--;
+                               _setTranslateX( _containerShiftIndex * _slideSize.x, tempHolder.el.style);
+                               self.setContent(tempHolder, _currentItemIndex + diffAbs - i - 1 - 1);
+                       }
+                       
+               }
+
+               // reset zoom/pan on previous item
+               if(_currZoomElementStyle && Math.abs(_indexDiff) === 1) {
+
+                       var prevItem = _getItemAt(_prevItemIndex);
+                       if(prevItem.initialZoomLevel !== _currZoomLevel) {
+                               _calculateItemSize(prevItem , _viewportSize );
+                               _setImageSize(prevItem);
+                               _applyZoomPanToItem( prevItem );                                
+                       }
+
+               }
+
+               // reset diff after update
+               _indexDiff = 0;
+
+               self.updateCurrZoomItem();
+
+               _prevItemIndex = _currentItemIndex;
+
+               _shout('afterChange');
+               
+       },
+
+
+
+       updateSize: function(force) {
+               
+               if(!_isFixedPosition && _options.modal) {
+                       var windowScrollY = framework.getScrollY();
+                       if(_currentWindowScrollY !== windowScrollY) {
+                               template.style.top = windowScrollY + 'px';
+                               _currentWindowScrollY = windowScrollY;
+                       }
+                       if(!force && _windowVisibleSize.x === window.innerWidth && _windowVisibleSize.y === window.innerHeight) {
+                               return;
+                       }
+                       _windowVisibleSize.x = window.innerWidth;
+                       _windowVisibleSize.y = window.innerHeight;
+
+                       //template.style.width = _windowVisibleSize.x + 'px';
+                       template.style.height = _windowVisibleSize.y + 'px';
+               }
+
+
+
+               _viewportSize.x = self.scrollWrap.clientWidth;
+               _viewportSize.y = self.scrollWrap.clientHeight;
+
+               _updatePageScrollOffset();
+
+               _slideSize.x = _viewportSize.x + Math.round(_viewportSize.x * _options.spacing);
+               _slideSize.y = _viewportSize.y;
+
+               _moveMainScroll(_slideSize.x * _currPositionIndex);
+
+               _shout('beforeResize'); // even may be used for example to switch image sources
+
+
+               // don't re-calculate size on inital size update
+               if(_containerShiftIndex !== undefined) {
+
+                       var holder,
+                               item,
+                               hIndex;
+
+                       for(var i = 0; i < NUM_HOLDERS; i++) {
+                               holder = _itemHolders[i];
+                               _setTranslateX( (i+_containerShiftIndex) * _slideSize.x, holder.el.style);
+
+                               hIndex = _currentItemIndex+i-1;
+
+                               if(_options.loop && _getNumItems() > 2) {
+                                       hIndex = _getLoopedId(hIndex);
+                               }
+
+                               // update zoom level on items and refresh source (if needsUpdate)
+                               item = _getItemAt( hIndex );
+
+                               // re-render gallery item if `needsUpdate`,
+                               // or doesn't have `bounds` (entirely new slide object)
+                               if( item && (_itemsNeedUpdate || item.needsUpdate || !item.bounds) ) {
+
+                                       self.cleanSlide( item );
+                                       
+                                       self.setContent( holder, hIndex );
+
+                                       // if "center" slide
+                                       if(i === 1) {
+                                               self.currItem = item;
+                                               self.updateCurrZoomItem(true);
+                                       }
+
+                                       item.needsUpdate = false;
+
+                               } else if(holder.index === -1 && hIndex >= 0) {
+                                       // add content first time
+                                       self.setContent( holder, hIndex );
+                               }
+                               if(item && item.container) {
+                                       _calculateItemSize(item, _viewportSize);
+                                       _setImageSize(item);
+                                       _applyZoomPanToItem( item );
+                               }
+                               
+                       }
+                       _itemsNeedUpdate = false;
+               }       
+
+               _startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel;
+               _currPanBounds = self.currItem.bounds;
+
+               if(_currPanBounds) {
+                       _panOffset.x = _currPanBounds.center.x;
+                       _panOffset.y = _currPanBounds.center.y;
+                       _applyCurrentZoomPan( true );
+               }
+               
+               _shout('resize');
+       },
+       
+       // Zoom current item to
+       zoomTo: function(destZoomLevel, centerPoint, speed, easingFn, updateFn) {
+               /*
+                       if(destZoomLevel === 'fit') {
+                               destZoomLevel = self.currItem.fitRatio;
+                       } else if(destZoomLevel === 'fill') {
+                               destZoomLevel = self.currItem.fillRatio;
+                       }
+               */
+
+               if(centerPoint) {
+                       _startZoomLevel = _currZoomLevel;
+                       _midZoomPoint.x = Math.abs(centerPoint.x) - _panOffset.x ;
+                       _midZoomPoint.y = Math.abs(centerPoint.y) - _panOffset.y ;
+                       _equalizePoints(_startPanOffset, _panOffset);
+               }
+
+               var destPanBounds = _calculatePanBounds(destZoomLevel, false),
+                       destPanOffset = {};
+
+               _modifyDestPanOffset('x', destPanBounds, destPanOffset, destZoomLevel);
+               _modifyDestPanOffset('y', destPanBounds, destPanOffset, destZoomLevel);
+
+               var initialZoomLevel = _currZoomLevel;
+               var initialPanOffset = {
+                       x: _panOffset.x,
+                       y: _panOffset.y
+               };
+
+               _roundPoint(destPanOffset);
+
+               var onUpdate = function(now) {
+                       if(now === 1) {
+                               _currZoomLevel = destZoomLevel;
+                               _panOffset.x = destPanOffset.x;
+                               _panOffset.y = destPanOffset.y;
+                       } else {
+                               _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel;
+                               _panOffset.x = (destPanOffset.x - initialPanOffset.x) * now + initialPanOffset.x;
+                               _panOffset.y = (destPanOffset.y - initialPanOffset.y) * now + initialPanOffset.y;
+                       }
+
+                       if(updateFn) {
+                               updateFn(now);
+                       }
+
+                       _applyCurrentZoomPan( now === 1 );
+               };
+
+               if(speed) {
+                       _animateProp('customZoomTo', 0, 1, speed, easingFn || framework.easing.sine.inOut, onUpdate);
+               } else {
+                       onUpdate(1);
+               }
+       }
+
+
+};
+
+
+/*>>core*/
+
+/*>>gestures*/
+/**
+ * Mouse/touch/pointer event handlers.
+ * 
+ * separated from @core.js for readability
+ */
+
+var MIN_SWIPE_DISTANCE = 30,
+       DIRECTION_CHECK_OFFSET = 10; // amount of pixels to drag to determine direction of swipe
+
+var _gestureStartTime,
+       _gestureCheckSpeedTime,
+
+       // pool of objects that are used during dragging of zooming
+       p = {}, // first point
+       p2 = {}, // second point (for zoom gesture)
+       delta = {},
+       _currPoint = {},
+       _startPoint = {},
+       _currPointers = [],
+       _startMainScrollPos = {},
+       _releaseAnimData,
+       _posPoints = [], // array of points during dragging, used to determine type of gesture
+       _tempPoint = {},
+
+       _isZoomingIn,
+       _verticalDragInitiated,
+       _oldAndroidTouchEndTimeout,
+       _currZoomedItemIndex = 0,
+       _centerPoint = _getEmptyPoint(),
+       _lastReleaseTime = 0,
+       _isDragging, // at least one pointer is down
+       _isMultitouch, // at least two _pointers are down
+       _zoomStarted, // zoom level changed during zoom gesture
+       _moved,
+       _dragAnimFrame,
+       _mainScrollShifted,
+       _currentPoints, // array of current touch points
+       _isZooming,
+       _currPointsDistance,
+       _startPointsDistance,
+       _currPanBounds,
+       _mainScrollPos = _getEmptyPoint(),
+       _currZoomElementStyle,
+       _mainScrollAnimating, // true, if animation after swipe gesture is running
+       _midZoomPoint = _getEmptyPoint(),
+       _currCenterPoint = _getEmptyPoint(),
+       _direction,
+       _isFirstMove,
+       _opacityChanged,
+       _bgOpacity,
+       _wasOverInitialZoom,
+
+       _isEqualPoints = function(p1, p2) {
+               return p1.x === p2.x && p1.y === p2.y;
+       },
+       _isNearbyPoints = function(touch0, touch1) {
+               return Math.abs(touch0.x - touch1.x) < DOUBLE_TAP_RADIUS && Math.abs(touch0.y - touch1.y) < DOUBLE_TAP_RADIUS;
+       },
+       _calculatePointsDistance = function(p1, p2) {
+               _tempPoint.x = Math.abs( p1.x - p2.x );
+               _tempPoint.y = Math.abs( p1.y - p2.y );
+               return Math.sqrt(_tempPoint.x * _tempPoint.x + _tempPoint.y * _tempPoint.y);
+       },
+       _stopDragUpdateLoop = function() {
+               if(_dragAnimFrame) {
+                       _cancelAF(_dragAnimFrame);
+                       _dragAnimFrame = null;
+               }
+       },
+       _dragUpdateLoop = function() {
+               if(_isDragging) {
+                       _dragAnimFrame = _requestAF(_dragUpdateLoop);
+                       _renderMovement();
+               }
+       },
+       _canPan = function() {
+               return !(_options.scaleMode === 'fit' && _currZoomLevel ===  self.currItem.initialZoomLevel);
+       },
+       
+       // find the closest parent DOM element
+       _closestElement = function(el, fn) {
+               if(!el || el === document) {
+                       return false;
+               }
+
+               // don't search elements above pswp__scroll-wrap
+               if(el.getAttribute('class') && el.getAttribute('class').indexOf('pswp__scroll-wrap') > -1 ) {
+                       return false;
+               }
+
+               if( fn(el) ) {
+                       return el;
+               }
+
+               return _closestElement(el.parentNode, fn);
+       },
+
+       _preventObj = {},
+       _preventDefaultEventBehaviour = function(e, isDown) {
+           _preventObj.prevent = !_closestElement(e.target, _options.isClickableElement);
+
+               _shout('preventDragEvent', e, isDown, _preventObj);
+               return _preventObj.prevent;
+
+       },
+       _convertTouchToPoint = function(touch, p) {
+               p.x = touch.pageX;
+               p.y = touch.pageY;
+               p.id = touch.identifier;
+               return p;
+       },
+       _findCenterOfPoints = function(p1, p2, pCenter) {
+               pCenter.x = (p1.x + p2.x) * 0.5;
+               pCenter.y = (p1.y + p2.y) * 0.5;
+       },
+       _pushPosPoint = function(time, x, y) {
+               if(time - _gestureCheckSpeedTime > 50) {
+                       var o = _posPoints.length > 2 ? _posPoints.shift() : {};
+                       o.x = x;
+                       o.y = y; 
+                       _posPoints.push(o);
+                       _gestureCheckSpeedTime = time;
+               }
+       },
+
+       _calculateVerticalDragOpacityRatio = function() {
+               var yOffset = _panOffset.y - self.currItem.initialPosition.y; // difference between initial and current position
+               return 1 -  Math.abs( yOffset / (_viewportSize.y / 2)  );
+       },
+
+       
+       // points pool, reused during touch events
+       _ePoint1 = {},
+       _ePoint2 = {},
+       _tempPointsArr = [],
+       _tempCounter,
+       _getTouchPoints = function(e) {
+               // clean up previous points, without recreating array
+               while(_tempPointsArr.length > 0) {
+                       _tempPointsArr.pop();
+               }
+
+               if(!_pointerEventEnabled) {
+                       if(e.type.indexOf('touch') > -1) {
+
+                               if(e.touches && e.touches.length > 0) {
+                                       _tempPointsArr[0] = _convertTouchToPoint(e.touches[0], _ePoint1);
+                                       if(e.touches.length > 1) {
+                                               _tempPointsArr[1] = _convertTouchToPoint(e.touches[1], _ePoint2);
+                                       }
+                               }
+                               
+                       } else {
+                               _ePoint1.x = e.pageX;
+                               _ePoint1.y = e.pageY;
+                               _ePoint1.id = '';
+                               _tempPointsArr[0] = _ePoint1;//_ePoint1;
+                       }
+               } else {
+                       _tempCounter = 0;
+                       // we can use forEach, as pointer events are supported only in modern browsers
+                       _currPointers.forEach(function(p) {
+                               if(_tempCounter === 0) {
+                                       _tempPointsArr[0] = p;
+                               } else if(_tempCounter === 1) {
+                                       _tempPointsArr[1] = p;
+                               }
+                               _tempCounter++;
+
+                       });
+               }
+               return _tempPointsArr;
+       },
+
+       _panOrMoveMainScroll = function(axis, delta) {
+
+               var panFriction,
+                       overDiff = 0,
+                       newOffset = _panOffset[axis] + delta[axis],
+                       startOverDiff,
+                       dir = delta[axis] > 0,
+                       newMainScrollPosition = _mainScrollPos.x + delta.x,
+                       mainScrollDiff = _mainScrollPos.x - _startMainScrollPos.x,
+                       newPanPos,
+                       newMainScrollPos;
+
+               // calculate fdistance over the bounds and friction
+               if(newOffset > _currPanBounds.min[axis] || newOffset < _currPanBounds.max[axis]) {
+                       panFriction = _options.panEndFriction;
+                       // Linear increasing of friction, so at 1/4 of viewport it's at max value. 
+                       // Looks not as nice as was expected. Left for history.
+                       // panFriction = (1 - (_panOffset[axis] + delta[axis] + panBounds.min[axis]) / (_viewportSize[axis] / 4) );
+               } else {
+                       panFriction = 1;
+               }
+               
+               newOffset = _panOffset[axis] + delta[axis] * panFriction;
+
+               // move main scroll or start panning
+               if(_options.allowPanToNext || _currZoomLevel === self.currItem.initialZoomLevel) {
+
+
+                       if(!_currZoomElementStyle) {
+                               
+                               newMainScrollPos = newMainScrollPosition;
+
+                       } else if(_direction === 'h' && axis === 'x' && !_zoomStarted ) {
+                               
+                               if(dir) {
+                                       if(newOffset > _currPanBounds.min[axis]) {
+                                               panFriction = _options.panEndFriction;
+                                               overDiff = _currPanBounds.min[axis] - newOffset;
+                                               startOverDiff = _currPanBounds.min[axis] - _startPanOffset[axis];
+                                       }
+                                       
+                                       // drag right
+                                       if( (startOverDiff <= 0 || mainScrollDiff < 0) && _getNumItems() > 1 ) {
+                                               newMainScrollPos = newMainScrollPosition;
+                                               if(mainScrollDiff < 0 && newMainScrollPosition > _startMainScrollPos.x) {
+                                                       newMainScrollPos = _startMainScrollPos.x;
+                                               }
+                                       } else {
+                                               if(_currPanBounds.min.x !== _currPanBounds.max.x) {
+                                                       newPanPos = newOffset;
+                                               }
+                                               
+                                       }
+
+                               } else {
+
+                                       if(newOffset < _currPanBounds.max[axis] ) {
+                                               panFriction =_options.panEndFriction;
+                                               overDiff = newOffset - _currPanBounds.max[axis];
+                                               startOverDiff = _startPanOffset[axis] - _currPanBounds.max[axis];
+                                       }
+
+                                       if( (startOverDiff <= 0 || mainScrollDiff > 0) && _getNumItems() > 1 ) {
+                                               newMainScrollPos = newMainScrollPosition;
+
+                                               if(mainScrollDiff > 0 && newMainScrollPosition < _startMainScrollPos.x) {
+                                                       newMainScrollPos = _startMainScrollPos.x;
+                                               }
+
+                                       } else {
+                                               if(_currPanBounds.min.x !== _currPanBounds.max.x) {
+                                                       newPanPos = newOffset;
+                                               }
+                                       }
+
+                               }
+
+
+                               //
+                       }
+
+                       if(axis === 'x') {
+
+                               if(newMainScrollPos !== undefined) {
+                                       _moveMainScroll(newMainScrollPos, true);
+                                       if(newMainScrollPos === _startMainScrollPos.x) {
+                                               _mainScrollShifted = false;
+                                       } else {
+                                               _mainScrollShifted = true;
+                                       }
+                               }
+
+                               if(_currPanBounds.min.x !== _currPanBounds.max.x) {
+                                       if(newPanPos !== undefined) {
+                                               _panOffset.x = newPanPos;
+                                       } else if(!_mainScrollShifted) {
+                                               _panOffset.x += delta.x * panFriction;
+                                       }
+                               }
+
+                               return newMainScrollPos !== undefined;
+                       }
+
+               }
+
+               if(!_mainScrollAnimating) {
+                       
+                       if(!_mainScrollShifted) {
+                               if(_currZoomLevel > self.currItem.fitRatio) {
+                                       _panOffset[axis] += delta[axis] * panFriction;
+                               
+                               }
+                       }
+
+                       
+               }
+               
+       },
+
+       // Pointerdown/touchstart/mousedown handler
+       _onDragStart = function(e) {
+
+               // Allow dragging only via left mouse button.
+               // As this handler is not added in IE8 - we ignore e.which
+               // 
+               // http://www.quirksmode.org/js/events_properties.html
+               // https://developer.mozilla.org/en-US/docs/Web/API/event.button
+               if(e.type === 'mousedown' && e.button > 0  ) {
+                       return;
+               }
+
+               if(_initialZoomRunning) {
+                       e.preventDefault();
+                       return;
+               }
+
+               if(_oldAndroidTouchEndTimeout && e.type === 'mousedown') {
+                       return;
+               }
+
+               if(_preventDefaultEventBehaviour(e, true)) {
+                       e.preventDefault();
+               }
+
+
+
+               _shout('pointerDown');
+
+               if(_pointerEventEnabled) {
+                       var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
+                       if(pointerIndex < 0) {
+                               pointerIndex = _currPointers.length;
+                       }
+                       _currPointers[pointerIndex] = {x:e.pageX, y:e.pageY, id: e.pointerId};
+               }
+               
+
+
+               var startPointsList = _getTouchPoints(e),
+                       numPoints = startPointsList.length;
+
+               _currentPoints = null;
+
+               _stopAllAnimations();
+
+               // init drag
+               if(!_isDragging || numPoints === 1) {
+
+                       
+
+                       _isDragging = _isFirstMove = true;
+                       framework.bind(window, _upMoveEvents, self);
+
+                       _isZoomingIn = 
+                               _wasOverInitialZoom = 
+                               _opacityChanged = 
+                               _verticalDragInitiated = 
+                               _mainScrollShifted = 
+                               _moved = 
+                               _isMultitouch = 
+                               _zoomStarted = false;
+
+                       _direction = null;
+
+                       _shout('firstTouchStart', startPointsList);
+
+                       _equalizePoints(_startPanOffset, _panOffset);
+
+                       _currPanDist.x = _currPanDist.y = 0;
+                       _equalizePoints(_currPoint, startPointsList[0]);
+                       _equalizePoints(_startPoint, _currPoint);
+
+                       //_equalizePoints(_startMainScrollPos, _mainScrollPos);
+                       _startMainScrollPos.x = _slideSize.x * _currPositionIndex;
+
+                       _posPoints = [{
+                               x: _currPoint.x,
+                               y: _currPoint.y
+                       }];
+
+                       _gestureCheckSpeedTime = _gestureStartTime = _getCurrentTime();
+
+                       //_mainScrollAnimationEnd(true);
+                       _calculatePanBounds( _currZoomLevel, true );
+                       
+                       // Start rendering
+                       _stopDragUpdateLoop();
+                       _dragUpdateLoop();
+                       
+               }
+
+               // init zoom
+               if(!_isZooming && numPoints > 1 && !_mainScrollAnimating && !_mainScrollShifted) {
+                       _startZoomLevel = _currZoomLevel;
+                       _zoomStarted = false; // true if zoom changed at least once
+
+                       _isZooming = _isMultitouch = true;
+                       _currPanDist.y = _currPanDist.x = 0;
+
+                       _equalizePoints(_startPanOffset, _panOffset);
+
+                       _equalizePoints(p, startPointsList[0]);
+                       _equalizePoints(p2, startPointsList[1]);
+
+                       _findCenterOfPoints(p, p2, _currCenterPoint);
+
+                       _midZoomPoint.x = Math.abs(_currCenterPoint.x) - _panOffset.x;
+                       _midZoomPoint.y = Math.abs(_currCenterPoint.y) - _panOffset.y;
+                       _currPointsDistance = _startPointsDistance = _calculatePointsDistance(p, p2);
+               }
+
+
+       },
+
+       // Pointermove/touchmove/mousemove handler
+       _onDragMove = function(e) {
+
+               e.preventDefault();
+
+               if(_pointerEventEnabled) {
+                       var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
+                       if(pointerIndex > -1) {
+                               var p = _currPointers[pointerIndex];
+                               p.x = e.pageX;
+                               p.y = e.pageY; 
+                       }
+               }
+
+               if(_isDragging) {
+                       var touchesList = _getTouchPoints(e);
+                       if(!_direction && !_moved && !_isZooming) {
+
+                               if(_mainScrollPos.x !== _slideSize.x * _currPositionIndex) {
+                                       // if main scroll position is shifted – direction is always horizontal
+                                       _direction = 'h';
+                               } else {
+                                       var diff = Math.abs(touchesList[0].x - _currPoint.x) - Math.abs(touchesList[0].y - _currPoint.y);
+                                       // check the direction of movement
+                                       if(Math.abs(diff) >= DIRECTION_CHECK_OFFSET) {
+                                               _direction = diff > 0 ? 'h' : 'v';
+                                               _currentPoints = touchesList;
+                                       }
+                               }
+                               
+                       } else {
+                               _currentPoints = touchesList;
+                       }
+               }       
+       },
+       // 
+       _renderMovement =  function() {
+
+               if(!_currentPoints) {
+                       return;
+               }
+
+               var numPoints = _currentPoints.length;
+
+               if(numPoints === 0) {
+                       return;
+               }
+
+               _equalizePoints(p, _currentPoints[0]);
+
+               delta.x = p.x - _currPoint.x;
+               delta.y = p.y - _currPoint.y;
+
+               if(_isZooming && numPoints > 1) {
+                       // Handle behaviour for more than 1 point
+
+                       _currPoint.x = p.x;
+                       _currPoint.y = p.y;
+               
+                       // check if one of two points changed
+                       if( !delta.x && !delta.y && _isEqualPoints(_currentPoints[1], p2) ) {
+                               return;
+                       }
+
+                       _equalizePoints(p2, _currentPoints[1]);
+
+
+                       if(!_zoomStarted) {
+                               _zoomStarted = true;
+                               _shout('zoomGestureStarted');
+                       }
+                       
+                       // Distance between two points
+                       var pointsDistance = _calculatePointsDistance(p,p2);
+
+                       var zoomLevel = _calculateZoomLevel(pointsDistance);
+
+                       // slightly over the of initial zoom level
+                       if(zoomLevel > self.currItem.initialZoomLevel + self.currItem.initialZoomLevel / 15) {
+                               _wasOverInitialZoom = true;
+                       }
+
+                       // Apply the friction if zoom level is out of the bounds
+                       var zoomFriction = 1,
+                               minZoomLevel = _getMinZoomLevel(),
+                               maxZoomLevel = _getMaxZoomLevel();
+
+                       if ( zoomLevel < minZoomLevel ) {
+                               
+                               if(_options.pinchToClose && !_wasOverInitialZoom && _startZoomLevel <= self.currItem.initialZoomLevel) {
+                                       // fade out background if zooming out
+                                       var minusDiff = minZoomLevel - zoomLevel;
+                                       var percent = 1 - minusDiff / (minZoomLevel / 1.2);
+
+                                       _applyBgOpacity(percent);
+                                       _shout('onPinchClose', percent);
+                                       _opacityChanged = true;
+                               } else {
+                                       zoomFriction = (minZoomLevel - zoomLevel) / minZoomLevel;
+                                       if(zoomFriction > 1) {
+                                               zoomFriction = 1;
+                                       }
+                                       zoomLevel = minZoomLevel - zoomFriction * (minZoomLevel / 3);
+                               }
+                               
+                       } else if ( zoomLevel > maxZoomLevel ) {
+                               // 1.5 - extra zoom level above the max. E.g. if max is x6, real max 6 + 1.5 = 7.5
+                               zoomFriction = (zoomLevel - maxZoomLevel) / ( minZoomLevel * 6 );
+                               if(zoomFriction > 1) {
+                                       zoomFriction = 1;
+                               }
+                               zoomLevel = maxZoomLevel + zoomFriction * minZoomLevel;
+                       }
+
+                       if(zoomFriction < 0) {
+                               zoomFriction = 0;
+                       }
+
+                       // distance between touch points after friction is applied
+                       _currPointsDistance = pointsDistance;
+
+                       // _centerPoint - The point in the middle of two pointers
+                       _findCenterOfPoints(p, p2, _centerPoint);
+               
+                       // paning with two pointers pressed
+                       _currPanDist.x += _centerPoint.x - _currCenterPoint.x;
+                       _currPanDist.y += _centerPoint.y - _currCenterPoint.y;
+                       _equalizePoints(_currCenterPoint, _centerPoint);
+
+                       _panOffset.x = _calculatePanOffset('x', zoomLevel);
+                       _panOffset.y = _calculatePanOffset('y', zoomLevel);
+
+                       _isZoomingIn = zoomLevel > _currZoomLevel;
+                       _currZoomLevel = zoomLevel;
+                       _applyCurrentZoomPan();
+
+               } else {
+
+                       // handle behaviour for one point (dragging or panning)
+
+                       if(!_direction) {
+                               return;
+                       }
+
+                       if(_isFirstMove) {
+                               _isFirstMove = false;
+
+                               // subtract drag distance that was used during the detection direction  
+
+                               if( Math.abs(delta.x) >= DIRECTION_CHECK_OFFSET) {
+                                       delta.x -= _currentPoints[0].x - _startPoint.x;
+                               }
+                               
+                               if( Math.abs(delta.y) >= DIRECTION_CHECK_OFFSET) {
+                                       delta.y -= _currentPoints[0].y - _startPoint.y;
+                               }
+                       }
+
+                       _currPoint.x = p.x;
+                       _currPoint.y = p.y;
+
+                       // do nothing if pointers position hasn't changed
+                       if(delta.x === 0 && delta.y === 0) {
+                               return;
+                       }
+
+                       if(_direction === 'v' && _options.closeOnVerticalDrag) {
+                               if(!_canPan()) {
+                                       _currPanDist.y += delta.y;
+                                       _panOffset.y += delta.y;
+
+                                       var opacityRatio = _calculateVerticalDragOpacityRatio();
+
+                                       _verticalDragInitiated = true;
+                                       _shout('onVerticalDrag', opacityRatio);
+
+                                       _applyBgOpacity(opacityRatio);
+                                       _applyCurrentZoomPan();
+                                       return ;
+                               }
+                       }
+
+                       _pushPosPoint(_getCurrentTime(), p.x, p.y);
+
+                       _moved = true;
+                       _currPanBounds = self.currItem.bounds;
+                       
+                       var mainScrollChanged = _panOrMoveMainScroll('x', delta);
+                       if(!mainScrollChanged) {
+                               _panOrMoveMainScroll('y', delta);
+
+                               _roundPoint(_panOffset);
+                               _applyCurrentZoomPan();
+                       }
+
+               }
+
+       },
+       
+       // Pointerup/pointercancel/touchend/touchcancel/mouseup event handler
+       _onDragRelease = function(e) {
+
+               if(_features.isOldAndroid ) {
+
+                       if(_oldAndroidTouchEndTimeout && e.type === 'mouseup') {
+                               return;
+                       }
+
+                       // on Android (v4.1, 4.2, 4.3 & possibly older) 
+                       // ghost mousedown/up event isn't preventable via e.preventDefault,
+                       // which causes fake mousedown event
+                       // so we block mousedown/up for 600ms
+                       if( e.type.indexOf('touch') > -1 ) {
+                               clearTimeout(_oldAndroidTouchEndTimeout);
+                               _oldAndroidTouchEndTimeout = setTimeout(function() {
+                                       _oldAndroidTouchEndTimeout = 0;
+                               }, 600);
+                       }
+                       
+               }
+
+               _shout('pointerUp');
+
+               if(_preventDefaultEventBehaviour(e, false)) {
+                       e.preventDefault();
+               }
+
+               var releasePoint;
+
+               if(_pointerEventEnabled) {
+                       var pointerIndex = framework.arraySearch(_currPointers, e.pointerId, 'id');
+                       
+                       if(pointerIndex > -1) {
+                               releasePoint = _currPointers.splice(pointerIndex, 1)[0];
+
+                               if(navigator.msPointerEnabled) {
+                                       var MSPOINTER_TYPES = {
+                                               4: 'mouse', // event.MSPOINTER_TYPE_MOUSE
+                                               2: 'touch', // event.MSPOINTER_TYPE_TOUCH 
+                                               3: 'pen' // event.MSPOINTER_TYPE_PEN
+                                       };
+                                       releasePoint.type = MSPOINTER_TYPES[e.pointerType];
+
+                                       if(!releasePoint.type) {
+                                               releasePoint.type = e.pointerType || 'mouse';
+                                       }
+                               } else {
+                                       releasePoint.type = e.pointerType || 'mouse';
+                               }
+
+                       }
+               }
+
+               var touchList = _getTouchPoints(e),
+                       gestureType,
+                       numPoints = touchList.length;
+
+               if(e.type === 'mouseup') {
+                       numPoints = 0;
+               }
+
+               // Do nothing if there were 3 touch points or more
+               if(numPoints === 2) {
+                       _currentPoints = null;
+                       return true;
+               }
+
+               // if second pointer released
+               if(numPoints === 1) {
+                       _equalizePoints(_startPoint, touchList[0]);
+               }                               
+
+
+               // pointer hasn't moved, send "tap release" point
+               if(numPoints === 0 && !_direction && !_mainScrollAnimating) {
+                       if(!releasePoint) {
+                               if(e.type === 'mouseup') {
+                                       releasePoint = {x: e.pageX, y: e.pageY, type:'mouse'};
+                               } else if(e.changedTouches && e.changedTouches[0]) {
+                                       releasePoint = {x: e.changedTouches[0].pageX, y: e.changedTouches[0].pageY, type:'touch'};
+                               }               
+                       }
+
+                       _shout('touchRelease', e, releasePoint);
+               }
+
+               // Difference in time between releasing of two last touch points (zoom gesture)
+               var releaseTimeDiff = -1;
+
+               // Gesture completed, no pointers left
+               if(numPoints === 0) {
+                       _isDragging = false;
+                       framework.unbind(window, _upMoveEvents, self);
+
+                       _stopDragUpdateLoop();
+
+                       if(_isZooming) {
+                               // Two points released at the same time
+                               releaseTimeDiff = 0;
+                       } else if(_lastReleaseTime !== -1) {
+                               releaseTimeDiff = _getCurrentTime() - _lastReleaseTime;
+                       }
+               }
+               _lastReleaseTime = numPoints === 1 ? _getCurrentTime() : -1;
+               
+               if(releaseTimeDiff !== -1 && releaseTimeDiff < 150) {
+                       gestureType = 'zoom';
+               } else {
+                       gestureType = 'swipe';
+               }
+
+               if(_isZooming && numPoints < 2) {
+                       _isZooming = false;
+
+                       // Only second point released
+                       if(numPoints === 1) {
+                               gestureType = 'zoomPointerUp';
+                       }
+                       _shout('zoomGestureEnded');
+               }
+
+               _currentPoints = null;
+               if(!_moved && !_zoomStarted && !_mainScrollAnimating && !_verticalDragInitiated) {
+                       // nothing to animate
+                       return;
+               }
+       
+               _stopAllAnimations();
+
+               
+               if(!_releaseAnimData) {
+                       _releaseAnimData = _initDragReleaseAnimationData();
+               }
+               
+               _releaseAnimData.calculateSwipeSpeed('x');
+
+
+               if(_verticalDragInitiated) {
+
+                       var opacityRatio = _calculateVerticalDragOpacityRatio();
+
+                       if(opacityRatio < _options.verticalDragRange) {
+                               self.close();
+                       } else {
+                               var initalPanY = _panOffset.y,
+                                       initialBgOpacity = _bgOpacity;
+
+                               _animateProp('verticalDrag', 0, 1, 300, framework.easing.cubic.out, function(now) {
+                                       
+                                       _panOffset.y = (self.currItem.initialPosition.y - initalPanY) * now + initalPanY;
+
+                                       _applyBgOpacity(  (1 - initialBgOpacity) * now + initialBgOpacity );
+                                       _applyCurrentZoomPan();
+                               });
+
+                               _shout('onVerticalDrag', 1);
+                       }
+
+                       return;
+               }
+
+
+               // main scroll 
+               if(  (_mainScrollShifted || _mainScrollAnimating) && numPoints === 0) {
+                       var itemChanged = _finishSwipeMainScrollGesture(gestureType, _releaseAnimData);
+                       if(itemChanged) {
+                               return;
+                       }
+                       gestureType = 'zoomPointerUp';
+               }
+
+               // prevent zoom/pan animation when main scroll animation runs
+               if(_mainScrollAnimating) {
+                       return;
+               }
+               
+               // Complete simple zoom gesture (reset zoom level if it's out of the bounds)  
+               if(gestureType !== 'swipe') {
+                       _completeZoomGesture();
+                       return;
+               }
+       
+               // Complete pan gesture if main scroll is not shifted, and it's possible to pan current image
+               if(!_mainScrollShifted && _currZoomLevel > self.currItem.fitRatio) {
+                       _completePanGesture(_releaseAnimData);
+               }
+       },
+
+
+       // Returns object with data about gesture
+       // It's created only once and then reused
+       _initDragReleaseAnimationData  = function() {
+               // temp local vars
+               var lastFlickDuration,
+                       tempReleasePos;
+
+               // s = this
+               var s = {
+                       lastFlickOffset: {},
+                       lastFlickDist: {},
+                       lastFlickSpeed: {},
+                       slowDownRatio:  {},
+                       slowDownRatioReverse:  {},
+                       speedDecelerationRatio:  {},
+                       speedDecelerationRatioAbs:  {},
+                       distanceOffset:  {},
+                       backAnimDestination: {},
+                       backAnimStarted: {},
+                       calculateSwipeSpeed: function(axis) {
+                               
+
+                               if( _posPoints.length > 1) {
+                                       lastFlickDuration = _getCurrentTime() - _gestureCheckSpeedTime + 50;
+                                       tempReleasePos = _posPoints[_posPoints.length-2][axis];
+                               } else {
+                                       lastFlickDuration = _getCurrentTime() - _gestureStartTime; // total gesture duration
+                                       tempReleasePos = _startPoint[axis];
+                               }
+                               s.lastFlickOffset[axis] = _currPoint[axis] - tempReleasePos;
+                               s.lastFlickDist[axis] = Math.abs(s.lastFlickOffset[axis]);
+                               if(s.lastFlickDist[axis] > 20) {
+                                       s.lastFlickSpeed[axis] = s.lastFlickOffset[axis] / lastFlickDuration;
+                               } else {
+                                       s.lastFlickSpeed[axis] = 0;
+                               }
+                               if( Math.abs(s.lastFlickSpeed[axis]) < 0.1 ) {
+                                       s.lastFlickSpeed[axis] = 0;
+                               }
+                               
+                               s.slowDownRatio[axis] = 0.95;
+                               s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
+                               s.speedDecelerationRatio[axis] = 1;
+                       },
+
+                       calculateOverBoundsAnimOffset: function(axis, speed) {
+                               if(!s.backAnimStarted[axis]) {
+
+                                       if(_panOffset[axis] > _currPanBounds.min[axis]) {
+                                               s.backAnimDestination[axis] = _currPanBounds.min[axis];
+                                               
+                                       } else if(_panOffset[axis] < _currPanBounds.max[axis]) {
+                                               s.backAnimDestination[axis] = _currPanBounds.max[axis];
+                                       }
+
+                                       if(s.backAnimDestination[axis] !== undefined) {
+                                               s.slowDownRatio[axis] = 0.7;
+                                               s.slowDownRatioReverse[axis] = 1 - s.slowDownRatio[axis];
+                                               if(s.speedDecelerationRatioAbs[axis] < 0.05) {
+
+                                                       s.lastFlickSpeed[axis] = 0;
+                                                       s.backAnimStarted[axis] = true;
+
+                                                       _animateProp('bounceZoomPan'+axis,_panOffset[axis], 
+                                                               s.backAnimDestination[axis], 
+                                                               speed || 300, 
+                                                               framework.easing.sine.out, 
+                                                               function(pos) {
+                                                                       _panOffset[axis] = pos;
+                                                                       _applyCurrentZoomPan();
+                                                               }
+                                                       );
+
+                                               }
+                                       }
+                               }
+                       },
+
+                       // Reduces the speed by slowDownRatio (per 10ms)
+                       calculateAnimOffset: function(axis) {
+                               if(!s.backAnimStarted[axis]) {
+                                       s.speedDecelerationRatio[axis] = s.speedDecelerationRatio[axis] * (s.slowDownRatio[axis] + 
+                                                                                               s.slowDownRatioReverse[axis] - 
+                                                                                               s.slowDownRatioReverse[axis] * s.timeDiff / 10);
+
+                                       s.speedDecelerationRatioAbs[axis] = Math.abs(s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis]);
+                                       s.distanceOffset[axis] = s.lastFlickSpeed[axis] * s.speedDecelerationRatio[axis] * s.timeDiff;
+                                       _panOffset[axis] += s.distanceOffset[axis];
+
+                               }
+                       },
+
+                       panAnimLoop: function() {
+                               if ( _animations.zoomPan ) {
+                                       _animations.zoomPan.raf = _requestAF(s.panAnimLoop);
+
+                                       s.now = _getCurrentTime();
+                                       s.timeDiff = s.now - s.lastNow;
+                                       s.lastNow = s.now;
+                                       
+                                       s.calculateAnimOffset('x');
+                                       s.calculateAnimOffset('y');
+
+                                       _applyCurrentZoomPan();
+                                       
+                                       s.calculateOverBoundsAnimOffset('x');
+                                       s.calculateOverBoundsAnimOffset('y');
+
+
+                                       if (s.speedDecelerationRatioAbs.x < 0.05 && s.speedDecelerationRatioAbs.y < 0.05) {
+
+                                               // round pan position
+                                               _panOffset.x = Math.round(_panOffset.x);
+                                               _panOffset.y = Math.round(_panOffset.y);
+                                               _applyCurrentZoomPan();
+                                               
+                                               _stopAnimation('zoomPan');
+                                               return;
+                                       }
+                               }
+
+                       }
+               };
+               return s;
+       },
+
+       _completePanGesture = function(animData) {
+               // calculate swipe speed for Y axis (paanning)
+               animData.calculateSwipeSpeed('y');
+
+               _currPanBounds = self.currItem.bounds;
+               
+               animData.backAnimDestination = {};
+               animData.backAnimStarted = {};
+
+               // Avoid acceleration animation if speed is too low
+               if(Math.abs(animData.lastFlickSpeed.x) <= 0.05 && Math.abs(animData.lastFlickSpeed.y) <= 0.05 ) {
+                       animData.speedDecelerationRatioAbs.x = animData.speedDecelerationRatioAbs.y = 0;
+
+                       // Run pan drag release animation. E.g. if you drag image and release finger without momentum.
+                       animData.calculateOverBoundsAnimOffset('x');
+                       animData.calculateOverBoundsAnimOffset('y');
+                       return true;
+               }
+
+               // Animation loop that controls the acceleration after pan gesture ends
+               _registerStartAnimation('zoomPan');
+               animData.lastNow = _getCurrentTime();
+               animData.panAnimLoop();
+       },
+
+
+       _finishSwipeMainScrollGesture = function(gestureType, _releaseAnimData) {
+               var itemChanged;
+               if(!_mainScrollAnimating) {
+                       _currZoomedItemIndex = _currentItemIndex;
+               }
+
+
+               
+               var itemsDiff;
+
+               if(gestureType === 'swipe') {
+                       var totalShiftDist = _currPoint.x - _startPoint.x,
+                               isFastLastFlick = _releaseAnimData.lastFlickDist.x < 10;
+
+                       // if container is shifted for more than MIN_SWIPE_DISTANCE, 
+                       // and last flick gesture was in right direction
+                       if(totalShiftDist > MIN_SWIPE_DISTANCE && 
+                               (isFastLastFlick || _releaseAnimData.lastFlickOffset.x > 20) ) {
+                               // go to prev item
+                               itemsDiff = -1;
+                       } else if(totalShiftDist < -MIN_SWIPE_DISTANCE && 
+                               (isFastLastFlick || _releaseAnimData.lastFlickOffset.x < -20) ) {
+                               // go to next item
+                               itemsDiff = 1;
+                       }
+               }
+
+               var nextCircle;
+
+               if(itemsDiff) {
+                       
+                       _currentItemIndex += itemsDiff;
+
+                       if(_currentItemIndex < 0) {
+                               _currentItemIndex = _options.loop ? _getNumItems()-1 : 0;
+                               nextCircle = true;
+                       } else if(_currentItemIndex >= _getNumItems()) {
+                               _currentItemIndex = _options.loop ? 0 : _getNumItems()-1;
+                               nextCircle = true;
+                       }
+
+                       if(!nextCircle || _options.loop) {
+                               _indexDiff += itemsDiff;
+                               _currPositionIndex -= itemsDiff;
+                               itemChanged = true;
+                       }
+                       
+
+                       
+               }
+
+               var animateToX = _slideSize.x * _currPositionIndex;
+               var animateToDist = Math.abs( animateToX - _mainScrollPos.x );
+               var finishAnimDuration;
+
+
+               if(!itemChanged && animateToX > _mainScrollPos.x !== _releaseAnimData.lastFlickSpeed.x > 0) {
+                       // "return to current" duration, e.g. when dragging from slide 0 to -1
+                       finishAnimDuration = 333; 
+               } else {
+                       finishAnimDuration = Math.abs(_releaseAnimData.lastFlickSpeed.x) > 0 ? 
+                                                                       animateToDist / Math.abs(_releaseAnimData.lastFlickSpeed.x) : 
+                                                                       333;
+
+                       finishAnimDuration = Math.min(finishAnimDuration, 400);
+                       finishAnimDuration = Math.max(finishAnimDuration, 250);
+               }
+
+               if(_currZoomedItemIndex === _currentItemIndex) {
+                       itemChanged = false;
+               }
+               
+               _mainScrollAnimating = true;
+               
+               _shout('mainScrollAnimStart');
+
+               _animateProp('mainScroll', _mainScrollPos.x, animateToX, finishAnimDuration, framework.easing.cubic.out, 
+                       _moveMainScroll,
+                       function() {
+                               _stopAllAnimations();
+                               _mainScrollAnimating = false;
+                               _currZoomedItemIndex = -1;
+                               
+                               if(itemChanged || _currZoomedItemIndex !== _currentItemIndex) {
+                                       self.updateCurrItem();
+                               }
+                               
+                               _shout('mainScrollAnimComplete');
+                       }
+               );
+
+               if(itemChanged) {
+                       self.updateCurrItem(true);
+               }
+
+               return itemChanged;
+       },
+
+       _calculateZoomLevel = function(touchesDistance) {
+               return  1 / _startPointsDistance * touchesDistance * _startZoomLevel;
+       },
+
+       // Resets zoom if it's out of bounds
+       _completeZoomGesture = function() {
+               var destZoomLevel = _currZoomLevel,
+                       minZoomLevel = _getMinZoomLevel(),
+                       maxZoomLevel = _getMaxZoomLevel();
+
+               if ( _currZoomLevel < minZoomLevel ) {
+                       destZoomLevel = minZoomLevel;
+               } else if ( _currZoomLevel > maxZoomLevel ) {
+                       destZoomLevel = maxZoomLevel;
+               }
+
+               var destOpacity = 1,
+                       onUpdate,
+                       initialOpacity = _bgOpacity;
+
+               if(_opacityChanged && !_isZoomingIn && !_wasOverInitialZoom && _currZoomLevel < minZoomLevel) {
+                       //_closedByScroll = true;
+                       self.close();
+                       return true;
+               }
+
+               if(_opacityChanged) {
+                       onUpdate = function(now) {
+                               _applyBgOpacity(  (destOpacity - initialOpacity) * now + initialOpacity );
+                       };
+               }
+
+               self.zoomTo(destZoomLevel, 0, 200,  framework.easing.cubic.out, onUpdate);
+               return true;
+       };
+
+
+_registerModule('Gestures', {
+       publicMethods: {
+
+               initGestures: function() {
+
+                       // helper function that builds touch/pointer/mouse events
+                       var addEventNames = function(pref, down, move, up, cancel) {
+                               _dragStartEvent = pref + down;
+                               _dragMoveEvent = pref + move;
+                               _dragEndEvent = pref + up;
+                               if(cancel) {
+                                       _dragCancelEvent = pref + cancel;
+                               } else {
+                                       _dragCancelEvent = '';
+                               }
+                       };
+
+                       _pointerEventEnabled = _features.pointerEvent;
+                       if(_pointerEventEnabled && _features.touch) {
+                               // we don't need touch events, if browser supports pointer events
+                               _features.touch = false;
+                       }
+
+                       if(_pointerEventEnabled) {
+                               if(navigator.msPointerEnabled) {
+                                       // IE10 pointer events are case-sensitive
+                                       addEventNames('MSPointer', 'Down', 'Move', 'Up', 'Cancel');
+                               } else {
+                                       addEventNames('pointer', 'down', 'move', 'up', 'cancel');
+                               }
+                       } else if(_features.touch) {
+                               addEventNames('touch', 'start', 'move', 'end', 'cancel');
+                               _likelyTouchDevice = true;
+                       } else {
+                               addEventNames('mouse', 'down', 'move', 'up');   
+                       }
+
+                       _upMoveEvents = _dragMoveEvent + ' ' + _dragEndEvent  + ' ' +  _dragCancelEvent;
+                       _downEvents = _dragStartEvent;
+
+                       if(_pointerEventEnabled && !_likelyTouchDevice) {
+                               _likelyTouchDevice = (navigator.maxTouchPoints > 1) || (navigator.msMaxTouchPoints > 1);
+                       }
+                       // make variable public
+                       self.likelyTouchDevice = _likelyTouchDevice; 
+                       
+                       _globalEventHandlers[_dragStartEvent] = _onDragStart;
+                       _globalEventHandlers[_dragMoveEvent] = _onDragMove;
+                       _globalEventHandlers[_dragEndEvent] = _onDragRelease; // the Kraken
+
+                       if(_dragCancelEvent) {
+                               _globalEventHandlers[_dragCancelEvent] = _globalEventHandlers[_dragEndEvent];
+                       }
+
+                       // Bind mouse events on device with detected hardware touch support, in case it supports multiple types of input.
+                       if(_features.touch) {
+                               _downEvents += ' mousedown';
+                               _upMoveEvents += ' mousemove mouseup';
+                               _globalEventHandlers.mousedown = _globalEventHandlers[_dragStartEvent];
+                               _globalEventHandlers.mousemove = _globalEventHandlers[_dragMoveEvent];
+                               _globalEventHandlers.mouseup = _globalEventHandlers[_dragEndEvent];
+                       }
+
+                       if(!_likelyTouchDevice) {
+                               // don't allow pan to next slide from zoomed state on Desktop
+                               _options.allowPanToNext = false;
+                       }
+               }
+
+       }
+});
+
+
+/*>>gestures*/
+
+/*>>show-hide-transition*/
+/**
+ * show-hide-transition.js:
+ *
+ * Manages initial opening or closing transition.
+ *
+ * If you're not planning to use transition for gallery at all,
+ * you may set options hideAnimationDuration and showAnimationDuration to 0,
+ * and just delete startAnimation function.
+ * 
+ */
+
+
+var _showOrHideTimeout,
+       _showOrHide = function(item, img, out, completeFn) {
+
+               if(_showOrHideTimeout) {
+                       clearTimeout(_showOrHideTimeout);
+               }
+
+               _initialZoomRunning = true;
+               _initialContentSet = true;
+               
+               // dimensions of small thumbnail {x:,y:,w:}.
+               // Height is optional, as calculated based on large image.
+               var thumbBounds; 
+               if(item.initialLayout) {
+                       thumbBounds = item.initialLayout;
+                       item.initialLayout = null;
+               } else {
+                       thumbBounds = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex);
+               }
+
+               var duration = out ? _options.hideAnimationDuration : _options.showAnimationDuration;
+
+               var onComplete = function() {
+                       _stopAnimation('initialZoom');
+                       if(!out) {
+                               _applyBgOpacity(1);
+                               if(img) {
+                                       img.style.display = 'block';
+                               }
+                               framework.addClass(template, 'pswp--animated-in');
+                               _shout('initialZoom' + (out ? 'OutEnd' : 'InEnd'));
+                       } else {
+                               self.template.removeAttribute('style');
+                               self.bg.removeAttribute('style');
+                       }
+
+                       if(completeFn) {
+                               completeFn();
+                       }
+                       _initialZoomRunning = false;
+               };
+
+               // if bounds aren't provided, just open gallery without animation
+               if(!duration || !thumbBounds || thumbBounds.x === undefined) {
+
+                       _shout('initialZoom' + (out ? 'Out' : 'In') );
+
+                       _currZoomLevel = item.initialZoomLevel;
+                       _equalizePoints(_panOffset,  item.initialPosition );
+                       _applyCurrentZoomPan();
+
+                       template.style.opacity = out ? 0 : 1;
+                       _applyBgOpacity(1);
+
+                       if(duration) {
+                               setTimeout(function() {
+                                       onComplete();
+                               }, duration);
+                       } else {
+                               onComplete();
+                       }
+
+                       return;
+               }
+
+               var startAnimation = function() {
+                       var closeWithRaf = _closedByScroll,
+                               fadeEverything = !self.currItem.src || self.currItem.loadError || _options.showHideOpacity;
+                       
+                       // apply hw-acceleration to image
+                       if(item.miniImg) {
+                               item.miniImg.style.webkitBackfaceVisibility = 'hidden';
+                       }
+
+                       if(!out) {
+                               _currZoomLevel = thumbBounds.w / item.w;
+                               _panOffset.x = thumbBounds.x;
+                               _panOffset.y = thumbBounds.y - _initalWindowScrollY;
+
+                               self[fadeEverything ? 'template' : 'bg'].style.opacity = 0.001;
+                               _applyCurrentZoomPan();
+                       }
+
+                       _registerStartAnimation('initialZoom');
+                       
+                       if(out && !closeWithRaf) {
+                               framework.removeClass(template, 'pswp--animated-in');
+                       }
+
+                       if(fadeEverything) {
+                               if(out) {
+                                       framework[ (closeWithRaf ? 'remove' : 'add') + 'Class' ](template, 'pswp--animate_opacity');
+                               } else {
+                                       setTimeout(function() {
+                                               framework.addClass(template, 'pswp--animate_opacity');
+                                       }, 30);
+                               }
+                       }
+
+                       _showOrHideTimeout = setTimeout(function() {
+
+                               _shout('initialZoom' + (out ? 'Out' : 'In') );
+                               
+
+                               if(!out) {
+
+                                       // "in" animation always uses CSS transitions (instead of rAF).
+                                       // CSS transition work faster here, 
+                                       // as developer may also want to animate other things, 
+                                       // like ui on top of sliding area, which can be animated just via CSS
+                                       
+                                       _currZoomLevel = item.initialZoomLevel;
+                                       _equalizePoints(_panOffset,  item.initialPosition );
+                                       _applyCurrentZoomPan();
+                                       _applyBgOpacity(1);
+
+                                       if(fadeEverything) {
+                                               template.style.opacity = 1;
+                                       } else {
+                                               _applyBgOpacity(1);
+                                       }
+
+                                       _showOrHideTimeout = setTimeout(onComplete, duration + 20);
+                               } else {
+
+                                       // "out" animation uses rAF only when PhotoSwipe is closed by browser scroll, to recalculate position
+                                       var destZoomLevel = thumbBounds.w / item.w,
+                                               initialPanOffset = {
+                                                       x: _panOffset.x,
+                                                       y: _panOffset.y
+                                               },
+                                               initialZoomLevel = _currZoomLevel,
+                                               initalBgOpacity = _bgOpacity,
+                                               onUpdate = function(now) {
+                                                       
+                                                       if(now === 1) {
+                                                               _currZoomLevel = destZoomLevel;
+                                                               _panOffset.x = thumbBounds.x;
+                                                               _panOffset.y = thumbBounds.y  - _currentWindowScrollY;
+                                                       } else {
+                                                               _currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel;
+                                                               _panOffset.x = (thumbBounds.x - initialPanOffset.x) * now + initialPanOffset.x;
+                                                               _panOffset.y = (thumbBounds.y - _currentWindowScrollY - initialPanOffset.y) * now + initialPanOffset.y;
+                                                       }
+                                                       
+                                                       _applyCurrentZoomPan();
+                                                       if(fadeEverything) {
+                                                               template.style.opacity = 1 - now;
+                                                       } else {
+                                                               _applyBgOpacity( initalBgOpacity - now * initalBgOpacity );
+                                                       }
+                                               };
+
+                                       if(closeWithRaf) {
+                                               _animateProp('initialZoom', 0, 1, duration, framework.easing.cubic.out, onUpdate, onComplete);
+                                       } else {
+                                               onUpdate(1);
+                                               _showOrHideTimeout = setTimeout(onComplete, duration + 20);
+                                       }
+                               }
+                       
+                       }, out ? 25 : 90); // Main purpose of this delay is to give browser time to paint and
+                                       // create composite layers of PhotoSwipe UI parts (background, controls, caption, arrows).
+                                       // Which avoids lag at the beginning of scale transition.
+               };
+               startAnimation();
+
+               
+       };
+
+/*>>show-hide-transition*/
+
+/*>>items-controller*/
+/**
+*
+* Controller manages gallery items, their dimensions, and their content.
+* 
+*/
+
+var _items,
+       _tempPanAreaSize = {},
+       _imagesToAppendPool = [],
+       _initialContentSet,
+       _initialZoomRunning,
+       _controllerDefaultOptions = {
+               index: 0,
+               errorMsg: '<div class="pswp__error-msg"><a href="%url%" target="_blank">The image</a> could not be loaded.</div>',
+               forceProgressiveLoading: false, // TODO
+               preload: [1,1],
+               getNumItemsFn: function() {
+                       return _items.length;
+               }
+       };
+
+
+var _getItemAt,
+       _getNumItems,
+       _initialIsLoop,
+       _getZeroBounds = function() {
+               return {
+                       center:{x:0,y:0}, 
+                       max:{x:0,y:0}, 
+                       min:{x:0,y:0}
+               };
+       },
+       _calculateSingleItemPanBounds = function(item, realPanElementW, realPanElementH ) {
+               var bounds = item.bounds;
+
+               // position of element when it's centered
+               bounds.center.x = Math.round((_tempPanAreaSize.x - realPanElementW) / 2);
+               bounds.center.y = Math.round((_tempPanAreaSize.y - realPanElementH) / 2) + item.vGap.top;
+
+               // maximum pan position
+               bounds.max.x = (realPanElementW > _tempPanAreaSize.x) ? 
+                                                       Math.round(_tempPanAreaSize.x - realPanElementW) : 
+                                                       bounds.center.x;
+               
+               bounds.max.y = (realPanElementH > _tempPanAreaSize.y) ? 
+                                                       Math.round(_tempPanAreaSize.y - realPanElementH) + item.vGap.top : 
+                                                       bounds.center.y;
+               
+               // minimum pan position
+               bounds.min.x = (realPanElementW > _tempPanAreaSize.x) ? 0 : bounds.center.x;
+               bounds.min.y = (realPanElementH > _tempPanAreaSize.y) ? item.vGap.top : bounds.center.y;
+       },
+       _calculateItemSize = function(item, viewportSize, zoomLevel) {
+
+               if (item.src && !item.loadError) {
+                       var isInitial = !zoomLevel;
+                       
+                       if(isInitial) {
+                               if(!item.vGap) {
+                                       item.vGap = {top:0,bottom:0};
+                               }
+                               // allows overriding vertical margin for individual items
+                               _shout('parseVerticalMargin', item);
+                       }
+
+
+                       _tempPanAreaSize.x = viewportSize.x;
+                       _tempPanAreaSize.y = viewportSize.y - item.vGap.top - item.vGap.bottom;
+
+                       if (isInitial) {
+                               var hRatio = _tempPanAreaSize.x / item.w;
+                               var vRatio = _tempPanAreaSize.y / item.h;
+
+                               item.fitRatio = hRatio < vRatio ? hRatio : vRatio;
+                               //item.fillRatio = hRatio > vRatio ? hRatio : vRatio;
+
+                               var scaleMode = _options.scaleMode;
+
+                               if (scaleMode === 'orig') {
+                                       zoomLevel = 1;
+                               } else if (scaleMode === 'fit') {
+                                       zoomLevel = item.fitRatio;
+                               }
+
+                               if (zoomLevel > 1) {
+                                       zoomLevel = 1;
+                               }
+
+                               item.initialZoomLevel = zoomLevel;
+                               
+                               if(!item.bounds) {
+                                       // reuse bounds object
+                                       item.bounds = _getZeroBounds(); 
+                               }
+                       }
+
+                       if(!zoomLevel) {
+                               return;
+                       }
+
+                       _calculateSingleItemPanBounds(item, item.w * zoomLevel, item.h * zoomLevel);
+
+                       if (isInitial && zoomLevel === item.initialZoomLevel) {
+                               item.initialPosition = item.bounds.center;
+                       }
+
+                       return item.bounds;
+               } else {
+                       item.w = item.h = 0;
+                       item.initialZoomLevel = item.fitRatio = 1;
+                       item.bounds = _getZeroBounds();
+                       item.initialPosition = item.bounds.center;
+
+                       // if it's not image, we return zero bounds (content is not zoomable)
+                       return item.bounds;
+               }
+               
+       },
+
+       
+
+
+       _appendImage = function(index, item, baseDiv, img, preventAnimation, keepPlaceholder) {
+               
+
+               if(item.loadError) {
+                       return;
+               }
+
+               if(img) {
+
+                       item.imageAppended = true;
+                       _setImageSize(item, img, (item === self.currItem && _renderMaxResolution) );
+                       
+                       baseDiv.appendChild(img);
+
+                       if(keepPlaceholder) {
+                               setTimeout(function() {
+                                       if(item && item.loaded && item.placeholder) {
+                                               item.placeholder.style.display = 'none';
+                                               item.placeholder = null;
+                                       }
+                               }, 500);
+                       }
+               }
+       },
+       
+
+
+       _preloadImage = function(item) {
+               item.loading = true;
+               item.loaded = false;
+               var img = item.img = framework.createEl('pswp__img', 'img');
+               var onComplete = function() {
+                       item.loading = false;
+                       item.loaded = true;
+
+                       if(item.loadComplete) {
+                               item.loadComplete(item);
+                       } else {
+                               item.img = null; // no need to store image object
+                       }
+                       img.onload = img.onerror = null;
+                       img = null;
+               };
+               img.onload = onComplete;
+               img.onerror = function() {
+                       item.loadError = true;
+                       onComplete();
+               };              
+
+               img.src = item.src;// + '?a=' + Math.random();
+
+               return img;
+       },
+       _checkForError = function(item, cleanUp) {
+               if(item.src && item.loadError && item.container) {
+
+                       if(cleanUp) {
+                               item.container.innerHTML = '';
+                       }
+
+                       item.container.innerHTML = _options.errorMsg.replace('%url%',  item.src );
+                       return true;
+                       
+               }
+       },
+       _setImageSize = function(item, img, maxRes) {
+               if(!item.src) {
+                       return;
+               }
+
+               if(!img) {
+                       img = item.container.lastChild;
+               }
+
+               var w = maxRes ? item.w : Math.round(item.w * item.fitRatio),
+                       h = maxRes ? item.h : Math.round(item.h * item.fitRatio);
+               
+               if(item.placeholder && !item.loaded) {
+                       item.placeholder.style.width = w + 'px';
+                       item.placeholder.style.height = h + 'px';
+               }
+
+               img.style.width = w + 'px';
+               img.style.height = h + 'px';
+       },
+       _appendImagesPool = function() {
+
+               if(_imagesToAppendPool.length) {
+                       var poolItem;
+
+                       for(var i = 0; i < _imagesToAppendPool.length; i++) {
+                               poolItem = _imagesToAppendPool[i];
+                               if( poolItem.holder.index === poolItem.index ) {
+                                       _appendImage(poolItem.index, poolItem.item, poolItem.baseDiv, poolItem.img, false, poolItem.clearPlaceholder);
+                               }
+                       }
+                       _imagesToAppendPool = [];
+               }
+       };
+       
+
+
+_registerModule('Controller', {
+
+       publicMethods: {
+
+               lazyLoadItem: function(index) {
+                       index = _getLoopedId(index);
+                       var item = _getItemAt(index);
+
+                       if(!item || ((item.loaded || item.loading) && !_itemsNeedUpdate)) {
+                               return;
+                       }
+
+                       _shout('gettingData', index, item);
+
+                       if (!item.src) {
+                               return;
+                       }
+
+                       _preloadImage(item);
+               },
+               initController: function() {
+                       framework.extend(_options, _controllerDefaultOptions, true);
+                       self.items = _items = items;
+                       _getItemAt = self.getItemAt;
+                       _getNumItems = _options.getNumItemsFn; //self.getNumItems;
+
+
+
+                       _initialIsLoop = _options.loop;
+                       if(_getNumItems() < 3) {
+                               _options.loop = false; // disable loop if less then 3 items
+                       }
+
+                       _listen('beforeChange', function(diff) {
+
+                               var p = _options.preload,
+                                       isNext = diff === null ? true : (diff >= 0),
+                                       preloadBefore = Math.min(p[0], _getNumItems() ),
+                                       preloadAfter = Math.min(p[1], _getNumItems() ),
+                                       i;
+
+
+                               for(i = 1; i <= (isNext ? preloadAfter : preloadBefore); i++) {
+                                       self.lazyLoadItem(_currentItemIndex+i);
+                               }
+                               for(i = 1; i <= (isNext ? preloadBefore : preloadAfter); i++) {
+                                       self.lazyLoadItem(_currentItemIndex-i);
+                               }
+                       });
+
+                       _listen('initialLayout', function() {
+                               self.currItem.initialLayout = _options.getThumbBoundsFn && _options.getThumbBoundsFn(_currentItemIndex);
+                       });
+
+                       _listen('mainScrollAnimComplete', _appendImagesPool);
+                       _listen('initialZoomInEnd', _appendImagesPool);
+
+
+
+                       _listen('destroy', function() {
+                               var item;
+                               for(var i = 0; i < _items.length; i++) {
+                                       item = _items[i];
+                                       // remove reference to DOM elements, for GC
+                                       if(item.container) {
+                                               item.container = null; 
+                                       }
+                                       if(item.placeholder) {
+                                               item.placeholder = null;
+                                       }
+                                       if(item.img) {
+                                               item.img = null;
+                                       }
+                                       if(item.preloader) {
+                                               item.preloader = null;
+                                       }
+                                       if(item.loadError) {
+                                               item.loaded = item.loadError = false;
+                                       }
+                               }
+                               _imagesToAppendPool = null;
+                       });
+               },
+
+
+               getItemAt: function(index) {
+                       if (index >= 0) {
+                               return _items[index] !== undefined ? _items[index] : false;
+                       }
+                       return false;
+               },
+
+               allowProgressiveImg: function() {
+                       // 1. Progressive image loading isn't working on webkit/blink 
+                       //    when hw-acceleration (e.g. translateZ) is applied to IMG element.
+                       //    That's why in PhotoSwipe parent element gets zoom transform, not image itself.
+                       //    
+                       // 2. Progressive image loading sometimes blinks in webkit/blink when applying animation to parent element.
+                       //    That's why it's disabled on touch devices (mainly because of swipe transition)
+                       //    
+                       // 3. Progressive image loading sometimes doesn't work in IE (up to 11).
+
+                       // Don't allow progressive loading on non-large touch devices
+                       return _options.forceProgressiveLoading || !_likelyTouchDevice || _options.mouseUsed || screen.width > 1200; 
+                       // 1200 - to eliminate touch devices with large screen (like Chromebook Pixel)
+               },
+
+               setContent: function(holder, index) {
+
+                       if(_options.loop) {
+                               index = _getLoopedId(index);
+                       }
+
+                       var prevItem = self.getItemAt(holder.index);
+                       if(prevItem) {
+                               prevItem.container = null;
+                       }
+       
+                       var item = self.getItemAt(index),
+                               img;
+                       
+                       if(!item) {
+                               holder.el.innerHTML = '';
+                               return;
+                       }
+
+                       // allow to override data
+                       _shout('gettingData', index, item);
+
+                       holder.index = index;
+                       holder.item = item;
+
+                       // base container DIV is created only once for each of 3 holders
+                       var baseDiv = item.container = framework.createEl('pswp__zoom-wrap'); 
+
+                       
+
+                       if(!item.src && item.html) {
+                               if(item.html.tagName) {
+                                       baseDiv.appendChild(item.html);
+                               } else {
+                                       baseDiv.innerHTML = item.html;
+                               }
+                       }
+
+                       _checkForError(item);
+
+                       _calculateItemSize(item, _viewportSize);
+                       
+                       if(item.src && !item.loadError && !item.loaded) {
+
+                               item.loadComplete = function(item) {
+
+                                       // gallery closed before image finished loading
+                                       if(!_isOpen) {
+                                               return;
+                                       }
+
+                                       // check if holder hasn't changed while image was loading
+                                       if(holder && holder.index === index ) {
+                                               if( _checkForError(item, true) ) {
+                                                       item.loadComplete = item.img = null;
+                                                       _calculateItemSize(item, _viewportSize);
+                                                       _applyZoomPanToItem(item);
+
+                                                       if(holder.index === _currentItemIndex) {
+                                                               // recalculate dimensions
+                                                               self.updateCurrZoomItem();
+                                                       }
+                                                       return;
+                                               }
+                                               if( !item.imageAppended ) {
+                                                       if(_features.transform && (_mainScrollAnimating || _initialZoomRunning) ) {
+                                                               _imagesToAppendPool.push({
+                                                                       item:item,
+                                                                       baseDiv:baseDiv,
+                                                                       img:item.img,
+                                                                       index:index,
+                                                                       holder:holder,
+                                                                       clearPlaceholder:true
+                                                               });
+                                                       } else {
+                                                               _appendImage(index, item, baseDiv, item.img, _mainScrollAnimating || _initialZoomRunning, true);
+                                                       }
+                                               } else {
+                                                       // remove preloader & mini-img
+                                                       if(!_initialZoomRunning && item.placeholder) {
+                                                               item.placeholder.style.display = 'none';
+                                                               item.placeholder = null;
+                                                       }
+                                               }
+                                       }
+
+                                       item.loadComplete = null;
+                                       item.img = null; // no need to store image element after it's added
+
+                                       _shout('imageLoadComplete', index, item);
+                               };
+
+                               if(framework.features.transform) {
+                                       
+                                       var placeholderClassName = 'pswp__img pswp__img--placeholder'; 
+                                       placeholderClassName += (item.msrc ? '' : ' pswp__img--placeholder--blank');
+
+                                       var placeholder = framework.createEl(placeholderClassName, item.msrc ? 'img' : '');
+                                       if(item.msrc) {
+                                               placeholder.src = item.msrc;
+                                       }
+                                       
+                                       _setImageSize(item, placeholder);
+
+                                       baseDiv.appendChild(placeholder);
+                                       item.placeholder = placeholder;
+
+                               }
+                               
+
+                               
+
+                               if(!item.loading) {
+                                       _preloadImage(item);
+                               }
+
+
+                               if( self.allowProgressiveImg() ) {
+                                       // just append image
+                                       if(!_initialContentSet && _features.transform) {
+                                               _imagesToAppendPool.push({
+                                                       item:item, 
+                                                       baseDiv:baseDiv, 
+                                                       img:item.img, 
+                                                       index:index, 
+                                                       holder:holder
+                                               });
+                                       } else {
+                                               _appendImage(index, item, baseDiv, item.img, true, true);
+                                       }
+                               }
+                               
+                       } else if(item.src && !item.loadError) {
+                               // image object is created every time, due to bugs of image loading & delay when switching images
+                               img = framework.createEl('pswp__img', 'img');
+                               img.style.opacity = 1;
+                               img.src = item.src;
+                               _setImageSize(item, img);
+                               _appendImage(index, item, baseDiv, img, true);
+                       }
+                       
+
+                       if(!_initialContentSet && index === _currentItemIndex) {
+                               _currZoomElementStyle = baseDiv.style;
+                               _showOrHide(item, (img ||item.img) );
+                       } else {
+                               _applyZoomPanToItem(item);
+                       }
+
+                       holder.el.innerHTML = '';
+                       holder.el.appendChild(baseDiv);
+               },
+
+               cleanSlide: function( item ) {
+                       if(item.img ) {
+                               item.img.onload = item.img.onerror = null;
+                       }
+                       item.loaded = item.loading = item.img = item.imageAppended = false;
+               }
+
+       }
+});
+
+/*>>items-controller*/
+
+/*>>tap*/
+/**
+ * tap.js:
+ *
+ * Displatches tap and double-tap events.
+ * 
+ */
+
+var tapTimer,
+       tapReleasePoint = {},
+       _dispatchTapEvent = function(origEvent, releasePoint, pointerType) {            
+               var e = document.createEvent( 'CustomEvent' ),
+                       eDetail = {
+                               origEvent:origEvent, 
+                               target:origEvent.target, 
+                               releasePoint: releasePoint, 
+                               pointerType:pointerType || 'touch'
+                       };
+
+               e.initCustomEvent( 'pswpTap', true, true, eDetail );
+               origEvent.target.dispatchEvent(e);
+       };
+
+_registerModule('Tap', {
+       publicMethods: {
+               initTap: function() {
+                       _listen('firstTouchStart', self.onTapStart);
+                       _listen('touchRelease', self.onTapRelease);
+                       _listen('destroy', function() {
+                               tapReleasePoint = {};
+                               tapTimer = null;
+                       });
+               },
+               onTapStart: function(touchList) {
+                       if(touchList.length > 1) {
+                               clearTimeout(tapTimer);
+                               tapTimer = null;
+                       }
+               },
+               onTapRelease: function(e, releasePoint) {
+                       if(!releasePoint) {
+                               return;
+                       }
+
+                       if(!_moved && !_isMultitouch && !_numAnimations) {
+                               var p0 = releasePoint;
+                               if(tapTimer) {
+                                       clearTimeout(tapTimer);
+                                       tapTimer = null;
+
+                                       // Check if taped on the same place
+                                       if ( _isNearbyPoints(p0, tapReleasePoint) ) {
+                                               _shout('doubleTap', p0);
+                                               return;
+                                       }
+                               }
+
+                               if(releasePoint.type === 'mouse') {
+                                       _dispatchTapEvent(e, releasePoint, 'mouse');
+                                       return;
+                               }
+
+                               var clickedTagName = e.target.tagName.toUpperCase();
+                               // avoid double tap delay on buttons and elements that have class pswp__single-tap
+                               if(clickedTagName === 'BUTTON' || framework.hasClass(e.target, 'pswp__single-tap') ) {
+                                       _dispatchTapEvent(e, releasePoint);
+                                       return;
+                               }
+
+                               _equalizePoints(tapReleasePoint, p0);
+
+                               tapTimer = setTimeout(function() {
+                                       _dispatchTapEvent(e, releasePoint);
+                                       tapTimer = null;
+                               }, 300);
+                       }
+               }
+       }
+});
+
+/*>>tap*/
+
+/*>>desktop-zoom*/
+/**
+ *
+ * desktop-zoom.js:
+ *
+ * - Binds mousewheel event for paning zoomed image.
+ * - Manages "dragging", "zoomed-in", "zoom-out" classes.
+ *   (which are used for cursors and zoom icon)
+ * - Adds toggleDesktopZoom function.
+ * 
+ */
+
+var _wheelDelta;
+       
+_registerModule('DesktopZoom', {
+
+       publicMethods: {
+
+               initDesktopZoom: function() {
+
+                       if(_oldIE) {
+                               // no zoom for old IE (<=8)
+                               return;
+                       }
+
+                       if(_likelyTouchDevice) {
+                               // if detected hardware touch support, we wait until mouse is used,
+                               // and only then apply desktop-zoom features
+                               _listen('mouseUsed', function() {
+                                       self.setupDesktopZoom();
+                               });
+                       } else {
+                               self.setupDesktopZoom(true);
+                       }
+
+               },
+
+               setupDesktopZoom: function(onInit) {
+
+                       _wheelDelta = {};
+
+                       var events = 'wheel mousewheel DOMMouseScroll';
+                       
+                       _listen('bindEvents', function() {
+                               framework.bind(template, events,  self.handleMouseWheel);
+                       });
+
+                       _listen('unbindEvents', function() {
+                               if(_wheelDelta) {
+                                       framework.unbind(template, events, self.handleMouseWheel);
+                               }
+                       });
+
+                       self.mouseZoomedIn = false;
+
+                       var hasDraggingClass,
+                               updateZoomable = function() {
+                                       if(self.mouseZoomedIn) {
+                                               framework.removeClass(template, 'pswp--zoomed-in');
+                                               self.mouseZoomedIn = false;
+                                       }
+                                       if(_currZoomLevel < 1) {
+                                               framework.addClass(template, 'pswp--zoom-allowed');
+                                       } else {
+                                               framework.removeClass(template, 'pswp--zoom-allowed');
+                                       }
+                                       removeDraggingClass();
+                               },
+                               removeDraggingClass = function() {
+                                       if(hasDraggingClass) {
+                                               framework.removeClass(template, 'pswp--dragging');
+                                               hasDraggingClass = false;
+                                       }
+                               };
+
+                       _listen('resize' , updateZoomable);
+                       _listen('afterChange' , updateZoomable);
+                       _listen('pointerDown', function() {
+                               if(self.mouseZoomedIn) {
+                                       hasDraggingClass = true;
+                                       framework.addClass(template, 'pswp--dragging');
+                               }
+                       });
+                       _listen('pointerUp', removeDraggingClass);
+
+                       if(!onInit) {
+                               updateZoomable();
+                       }
+                       
+               },
+
+               handleMouseWheel: function(e) {
+
+                       if(_currZoomLevel <= self.currItem.fitRatio) {
+                               if( _options.modal ) {
+
+                                       if (!_options.closeOnScroll || _numAnimations || _isDragging) {
+                                               e.preventDefault();
+                                       } else if(_transformKey && Math.abs(e.deltaY) > 2) {
+                                               // close PhotoSwipe
+                                               // if browser supports transforms & scroll changed enough
+                                               _closedByScroll = true;
+                                               self.close();
+                                       }
+
+                               }
+                               return true;
+                       }
+
+                       // allow just one event to fire
+                       e.stopPropagation();
+
+                       // https://developer.mozilla.org/en-US/docs/Web/Events/wheel
+                       _wheelDelta.x = 0;
+
+                       if('deltaX' in e) {
+                               if(e.deltaMode === 1 /* DOM_DELTA_LINE */) {
+                                       // 18 - average line height
+                                       _wheelDelta.x = e.deltaX * 18;
+                                       _wheelDelta.y = e.deltaY * 18;
+                               } else {
+                                       _wheelDelta.x = e.deltaX;
+                                       _wheelDelta.y = e.deltaY;
+                               }
+                       } else if('wheelDelta' in e) {
+                               if(e.wheelDeltaX) {
+                                       _wheelDelta.x = -0.16 * e.wheelDeltaX;
+                               }
+                               if(e.wheelDeltaY) {
+                                       _wheelDelta.y = -0.16 * e.wheelDeltaY;
+                               } else {
+                                       _wheelDelta.y = -0.16 * e.wheelDelta;
+                               }
+                       } else if('detail' in e) {
+                               _wheelDelta.y = e.detail;
+                       } else {
+                               return;
+                       }
+
+                       _calculatePanBounds(_currZoomLevel, true);
+
+                       var newPanX = _panOffset.x - _wheelDelta.x,
+                               newPanY = _panOffset.y - _wheelDelta.y;
+
+                       // only prevent scrolling in nonmodal mode when not at edges
+                       if (_options.modal ||
+                               (
+                               newPanX <= _currPanBounds.min.x && newPanX >= _currPanBounds.max.x &&
+                               newPanY <= _currPanBounds.min.y && newPanY >= _currPanBounds.max.y
+                               ) ) {
+                               e.preventDefault();
+                       }
+
+                       // TODO: use rAF instead of mousewheel?
+                       self.panTo(newPanX, newPanY);
+               },
+
+               toggleDesktopZoom: function(centerPoint) {
+                       centerPoint = centerPoint || {x:_viewportSize.x/2 + _offset.x, y:_viewportSize.y/2 + _offset.y };
+
+                       var doubleTapZoomLevel = _options.getDoubleTapZoom(true, self.currItem);
+                       var zoomOut = _currZoomLevel === doubleTapZoomLevel;
+                       
+                       self.mouseZoomedIn = !zoomOut;
+
+                       self.zoomTo(zoomOut ? self.currItem.initialZoomLevel : doubleTapZoomLevel, centerPoint, 333);
+                       framework[ (!zoomOut ? 'add' : 'remove') + 'Class'](template, 'pswp--zoomed-in');
+               }
+
+       }
+});
+
+
+/*>>desktop-zoom*/
+
+/*>>history*/
+/**
+ *
+ * history.js:
+ *
+ * - Back button to close gallery.
+ * 
+ * - Unique URL for each slide: example.com/&pid=1&gid=3
+ *   (where PID is picture index, and GID and gallery index)
+ *   
+ * - Switch URL when slides change.
+ * 
+ */
+
+
+var _historyDefaultOptions = {
+       history: true,
+       galleryUID: 1
+};
+
+var _historyUpdateTimeout,
+       _hashChangeTimeout,
+       _hashAnimCheckTimeout,
+       _hashChangedByScript,
+       _hashChangedByHistory,
+       _hashReseted,
+       _initialHash,
+       _historyChanged,
+       _closedFromURL,
+       _urlChangedOnce,
+       _windowLoc,
+
+       _supportsPushState,
+
+       _getHash = function() {
+               return _windowLoc.hash.substring(1);
+       },
+       _cleanHistoryTimeouts = function() {
+
+               if(_historyUpdateTimeout) {
+                       clearTimeout(_historyUpdateTimeout);
+               }
+
+               if(_hashAnimCheckTimeout) {
+                       clearTimeout(_hashAnimCheckTimeout);
+               }
+       },
+
+       // pid - Picture index
+       // gid - Gallery index
+       _parseItemIndexFromURL = function() {
+               var hash = _getHash(),
+                       params = {};
+
+               if(hash.length < 5) { // pid=1
+                       return params;
+               }
+
+               var i, vars = hash.split('&');
+               for (i = 0; i < vars.length; i++) {
+                       if(!vars[i]) {
+                               continue;
+                       }
+                       var pair = vars[i].split('=');  
+                       if(pair.length < 2) {
+                               continue;
+                       }
+                       params[pair[0]] = pair[1];
+               }
+               if(_options.galleryPIDs) {
+                       // detect custom pid in hash and search for it among the items collection
+                       var searchfor = params.pid;
+                       params.pid = 0; // if custom pid cannot be found, fallback to the first item
+                       for(i = 0; i < _items.length; i++) {
+                               if(_items[i].pid === searchfor) {
+                                       params.pid = i;
+                                       break;
+                               }
+                       }
+               } else {
+                       params.pid = parseInt(params.pid,10)-1;
+               }
+               if( params.pid < 0 ) {
+                       params.pid = 0;
+               }
+               return params;
+       },
+       _updateHash = function() {
+
+               if(_hashAnimCheckTimeout) {
+                       clearTimeout(_hashAnimCheckTimeout);
+               }
+
+
+               if(_numAnimations || _isDragging) {
+                       // changing browser URL forces layout/paint in some browsers, which causes noticable lag during animation
+                       // that's why we update hash only when no animations running
+                       _hashAnimCheckTimeout = setTimeout(_updateHash, 500);
+                       return;
+               }
+               
+               if(_hashChangedByScript) {
+                       clearTimeout(_hashChangeTimeout);
+               } else {
+                       _hashChangedByScript = true;
+               }
+
+
+               var pid = (_currentItemIndex + 1);
+               var item = _getItemAt( _currentItemIndex );
+               if(item.hasOwnProperty('pid')) {
+                       // carry forward any custom pid assigned to the item
+                       pid = item.pid;
+               }
+               var newHash = _initialHash + '&'  +  'gid=' + _options.galleryUID + '&' + 'pid=' + pid;
+
+               if(!_historyChanged) {
+                       if(_windowLoc.hash.indexOf(newHash) === -1) {
+                               _urlChangedOnce = true;
+                       }
+                       // first time - add new hisory record, then just replace
+               }
+
+               var newURL = _windowLoc.href.split('#')[0] + '#' +  newHash;
+
+               if( _supportsPushState ) {
+
+                       if('#' + newHash !== window.location.hash) {
+                               history[_historyChanged ? 'replaceState' : 'pushState']('', document.title, newURL);
+                       }
+
+               } else {
+                       if(_historyChanged) {
+                               _windowLoc.replace( newURL );
+                       } else {
+                               _windowLoc.hash = newHash;
+                       }
+               }
+               
+               
+
+               _historyChanged = true;
+               _hashChangeTimeout = setTimeout(function() {
+                       _hashChangedByScript = false;
+               }, 60);
+       };
+
+
+
+       
+
+_registerModule('History', {
+
+       
+
+       publicMethods: {
+               initHistory: function() {
+
+                       framework.extend(_options, _historyDefaultOptions, true);
+
+                       if( !_options.history ) {
+                               return;
+                       }
+
+
+                       _windowLoc = window.location;
+                       _urlChangedOnce = false;
+                       _closedFromURL = false;
+                       _historyChanged = false;
+                       _initialHash = _getHash();
+                       _supportsPushState = ('pushState' in history);
+
+
+                       if(_initialHash.indexOf('gid=') > -1) {
+                               _initialHash = _initialHash.split('&gid=')[0];
+                               _initialHash = _initialHash.split('?gid=')[0];
+                       }
+                       
+
+                       _listen('afterChange', self.updateURL);
+                       _listen('unbindEvents', function() {
+                               framework.unbind(window, 'hashchange', self.onHashChange);
+                       });
+
+
+                       var returnToOriginal = function() {
+                               _hashReseted = true;
+                               if(!_closedFromURL) {
+
+                                       if(_urlChangedOnce) {
+                                               history.back();
+                                       } else {
+
+                                               if(_initialHash) {
+                                                       _windowLoc.hash = _initialHash;
+                                               } else {
+                                                       if (_supportsPushState) {
+
+                                                               // remove hash from url without refreshing it or scrolling to top
+                                                               history.pushState('', document.title,  _windowLoc.pathname + _windowLoc.search );
+                                                       } else {
+                                                               _windowLoc.hash = '';
+                                                       }
+                                               }
+                                       }
+                                       
+                               }
+
+                               _cleanHistoryTimeouts();
+                       };
+
+
+                       _listen('unbindEvents', function() {
+                               if(_closedByScroll) {
+                                       // if PhotoSwipe is closed by scroll, we go "back" before the closing animation starts
+                                       // this is done to keep the scroll position
+                                       returnToOriginal();
+                               }
+                       });
+                       _listen('destroy', function() {
+                               if(!_hashReseted) {
+                                       returnToOriginal();
+                               }
+                       });
+                       _listen('firstUpdate', function() {
+                               _currentItemIndex = _parseItemIndexFromURL().pid;
+                       });
+
+                       
+
+                       
+                       var index = _initialHash.indexOf('pid=');
+                       if(index > -1) {
+                               _initialHash = _initialHash.substring(0, index);
+                               if(_initialHash.slice(-1) === '&') {
+                                       _initialHash = _initialHash.slice(0, -1);
+                               }
+                       }
+                       
+
+                       setTimeout(function() {
+                               if(_isOpen) { // hasn't destroyed yet
+                                       framework.bind(window, 'hashchange', self.onHashChange);
+                               }
+                       }, 40);
+                       
+               },
+               onHashChange: function() {
+
+                       if(_getHash() === _initialHash) {
+
+                               _closedFromURL = true;
+                               self.close();
+                               return;
+                       }
+                       if(!_hashChangedByScript) {
+
+                               _hashChangedByHistory = true;
+                               self.goTo( _parseItemIndexFromURL().pid );
+                               _hashChangedByHistory = false;
+                       }
+                       
+               },
+               updateURL: function() {
+
+                       // Delay the update of URL, to avoid lag during transition, 
+                       // and to not to trigger actions like "refresh page sound" or "blinking favicon" to often
+                       
+                       _cleanHistoryTimeouts();
+                       
+
+                       if(_hashChangedByHistory) {
+                               return;
+                       }
+
+                       if(!_historyChanged) {
+                               _updateHash(); // first time
+                       } else {
+                               _historyUpdateTimeout = setTimeout(_updateHash, 800);
+                       }
+               }
+       
+       }
+});
+
+
+/*>>history*/
+       framework.extend(self, publicMethods); };
+       return PhotoSwipe;
+});
\ No newline at end of file
diff --git a/website/js/vendor/photoswipe/photoswipe.min.js b/website/js/vendor/photoswipe/photoswipe.min.js
new file mode 100644 (file)
index 0000000..0370861
--- /dev/null
@@ -0,0 +1,4 @@
+/*! PhotoSwipe - v4.1.3 - 2019-01-08
+* http://photoswipe.com
+* Copyright (c) 2019 Dmitry Semenov; */
+!function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.PhotoSwipe=b()}(this,function(){"use strict";var a=function(a,b,c,d){var e={features:null,bind:function(a,b,c,d){var e=(d?"remove":"add")+"EventListener";b=b.split(" ");for(var f=0;f<b.length;f++)b[f]&&a[e](b[f],c,!1)},isArray:function(a){return a instanceof Array},createEl:function(a,b){var c=document.createElement(b||"div");return a&&(c.className=a),c},getScrollY:function(){var a=window.pageYOffset;return void 0!==a?a:document.documentElement.scrollTop},unbind:function(a,b,c){e.bind(a,b,c,!0)},removeClass:function(a,b){var c=new RegExp("(\\s|^)"+b+"(\\s|$)");a.className=a.className.replace(c," ").replace(/^\s\s*/,"").replace(/\s\s*$/,"")},addClass:function(a,b){e.hasClass(a,b)||(a.className+=(a.className?" ":"")+b)},hasClass:function(a,b){return a.className&&new RegExp("(^|\\s)"+b+"(\\s|$)").test(a.className)},getChildByClass:function(a,b){for(var c=a.firstChild;c;){if(e.hasClass(c,b))return c;c=c.nextSibling}},arraySearch:function(a,b,c){for(var d=a.length;d--;)if(a[d][c]===b)return d;return-1},extend:function(a,b,c){for(var d in b)if(b.hasOwnProperty(d)){if(c&&a.hasOwnProperty(d))continue;a[d]=b[d]}},easing:{sine:{out:function(a){return Math.sin(a*(Math.PI/2))},inOut:function(a){return-(Math.cos(Math.PI*a)-1)/2}},cubic:{out:function(a){return--a*a*a+1}}},detectFeatures:function(){if(e.features)return e.features;var a=e.createEl(),b=a.style,c="",d={};if(d.oldIE=document.all&&!document.addEventListener,d.touch="ontouchstart"in window,window.requestAnimationFrame&&(d.raf=window.requestAnimationFrame,d.caf=window.cancelAnimationFrame),d.pointerEvent=!!window.PointerEvent||navigator.msPointerEnabled,!d.pointerEvent){var f=navigator.userAgent;if(/iP(hone|od)/.test(navigator.platform)){var g=navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);g&&g.length>0&&(g=parseInt(g[1],10),g>=1&&g<8&&(d.isOldIOSPhone=!0))}var h=f.match(/Android\s([0-9\.]*)/),i=h?h[1]:0;i=parseFloat(i),i>=1&&(i<4.4&&(d.isOldAndroid=!0),d.androidVersion=i),d.isMobileOpera=/opera mini|opera mobi/i.test(f)}for(var j,k,l=["transform","perspective","animationName"],m=["","webkit","Moz","ms","O"],n=0;n<4;n++){c=m[n];for(var o=0;o<3;o++)j=l[o],k=c+(c?j.charAt(0).toUpperCase()+j.slice(1):j),!d[j]&&k in b&&(d[j]=k);c&&!d.raf&&(c=c.toLowerCase(),d.raf=window[c+"RequestAnimationFrame"],d.raf&&(d.caf=window[c+"CancelAnimationFrame"]||window[c+"CancelRequestAnimationFrame"]))}if(!d.raf){var p=0;d.raf=function(a){var b=(new Date).getTime(),c=Math.max(0,16-(b-p)),d=window.setTimeout(function(){a(b+c)},c);return p=b+c,d},d.caf=function(a){clearTimeout(a)}}return d.svg=!!document.createElementNS&&!!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,e.features=d,d}};e.detectFeatures(),e.features.oldIE&&(e.bind=function(a,b,c,d){b=b.split(" ");for(var e,f=(d?"detach":"attach")+"Event",g=function(){c.handleEvent.call(c)},h=0;h<b.length;h++)if(e=b[h])if("object"==typeof c&&c.handleEvent){if(d){if(!c["oldIE"+e])return!1}else c["oldIE"+e]=g;a[f]("on"+e,c["oldIE"+e])}else a[f]("on"+e,c)});var f=this,g=25,h=3,i={allowPanToNext:!0,spacing:.12,bgOpacity:1,mouseUsed:!1,loop:!0,pinchToClose:!0,closeOnScroll:!0,closeOnVerticalDrag:!0,verticalDragRange:.75,hideAnimationDuration:333,showAnimationDuration:333,showHideOpacity:!1,focus:!0,escKey:!0,arrowKeys:!0,mainScrollEndFriction:.35,panEndFriction:.35,isClickableElement:function(a){return"A"===a.tagName},getDoubleTapZoom:function(a,b){return a?1:b.initialZoomLevel<.7?1:1.33},maxSpreadZoom:1.33,modal:!0,scaleMode:"fit"};e.extend(i,d);var j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,_,aa,ba,ca,da,ea,fa,ga,ha,ia,ja,ka,la,ma=function(){return{x:0,y:0}},na=ma(),oa=ma(),pa=ma(),qa={},ra=0,sa={},ta=ma(),ua=0,va=!0,wa=[],xa={},ya=!1,za=function(a,b){e.extend(f,b.publicMethods),wa.push(a)},Aa=function(a){var b=ac();return a>b-1?a-b:a<0?b+a:a},Ba={},Ca=function(a,b){return Ba[a]||(Ba[a]=[]),Ba[a].push(b)},Da=function(a){var b=Ba[a];if(b){var c=Array.prototype.slice.call(arguments);c.shift();for(var d=0;d<b.length;d++)b[d].apply(f,c)}},Ea=function(){return(new Date).getTime()},Fa=function(a){ja=a,f.bg.style.opacity=a*i.bgOpacity},Ga=function(a,b,c,d,e){(!ya||e&&e!==f.currItem)&&(d/=e?e.fitRatio:f.currItem.fitRatio),a[E]=u+b+"px, "+c+"px"+v+" scale("+d+")"},Ha=function(a){ea&&(a&&(s>f.currItem.fitRatio?ya||(mc(f.currItem,!1,!0),ya=!0):ya&&(mc(f.currItem),ya=!1)),Ga(ea,pa.x,pa.y,s))},Ia=function(a){a.container&&Ga(a.container.style,a.initialPosition.x,a.initialPosition.y,a.initialZoomLevel,a)},Ja=function(a,b){b[E]=u+a+"px, 0px"+v},Ka=function(a,b){if(!i.loop&&b){var c=m+(ta.x*ra-a)/ta.x,d=Math.round(a-tb.x);(c<0&&d>0||c>=ac()-1&&d<0)&&(a=tb.x+d*i.mainScrollEndFriction)}tb.x=a,Ja(a,n)},La=function(a,b){var c=ub[a]-sa[a];return oa[a]+na[a]+c-c*(b/t)},Ma=function(a,b){a.x=b.x,a.y=b.y,b.id&&(a.id=b.id)},Na=function(a){a.x=Math.round(a.x),a.y=Math.round(a.y)},Oa=null,Pa=function(){Oa&&(e.unbind(document,"mousemove",Pa),e.addClass(a,"pswp--has_mouse"),i.mouseUsed=!0,Da("mouseUsed")),Oa=setTimeout(function(){Oa=null},100)},Qa=function(){e.bind(document,"keydown",f),N.transform&&e.bind(f.scrollWrap,"click",f),i.mouseUsed||e.bind(document,"mousemove",Pa),e.bind(window,"resize scroll orientationchange",f),Da("bindEvents")},Ra=function(){e.unbind(window,"resize scroll orientationchange",f),e.unbind(window,"scroll",r.scroll),e.unbind(document,"keydown",f),e.unbind(document,"mousemove",Pa),N.transform&&e.unbind(f.scrollWrap,"click",f),V&&e.unbind(window,p,f),clearTimeout(O),Da("unbindEvents")},Sa=function(a,b){var c=ic(f.currItem,qa,a);return b&&(da=c),c},Ta=function(a){return a||(a=f.currItem),a.initialZoomLevel},Ua=function(a){return a||(a=f.currItem),a.w>0?i.maxSpreadZoom:1},Va=function(a,b,c,d){return d===f.currItem.initialZoomLevel?(c[a]=f.currItem.initialPosition[a],!0):(c[a]=La(a,d),c[a]>b.min[a]?(c[a]=b.min[a],!0):c[a]<b.max[a]&&(c[a]=b.max[a],!0))},Wa=function(){if(E){var b=N.perspective&&!G;return u="translate"+(b?"3d(":"("),void(v=N.perspective?", 0px)":")")}E="left",e.addClass(a,"pswp--ie"),Ja=function(a,b){b.left=a+"px"},Ia=function(a){var b=a.fitRatio>1?1:a.fitRatio,c=a.container.style,d=b*a.w,e=b*a.h;c.width=d+"px",c.height=e+"px",c.left=a.initialPosition.x+"px",c.top=a.initialPosition.y+"px"},Ha=function(){if(ea){var a=ea,b=f.currItem,c=b.fitRatio>1?1:b.fitRatio,d=c*b.w,e=c*b.h;a.width=d+"px",a.height=e+"px",a.left=pa.x+"px",a.top=pa.y+"px"}}},Xa=function(a){var b="";i.escKey&&27===a.keyCode?b="close":i.arrowKeys&&(37===a.keyCode?b="prev":39===a.keyCode&&(b="next")),b&&(a.ctrlKey||a.altKey||a.shiftKey||a.metaKey||(a.preventDefault?a.preventDefault():a.returnValue=!1,f[b]()))},Ya=function(a){a&&(Y||X||fa||T)&&(a.preventDefault(),a.stopPropagation())},Za=function(){f.setScrollOffset(0,e.getScrollY())},$a={},_a=0,ab=function(a){$a[a]&&($a[a].raf&&I($a[a].raf),_a--,delete $a[a])},bb=function(a){$a[a]&&ab(a),$a[a]||(_a++,$a[a]={})},cb=function(){for(var a in $a)$a.hasOwnProperty(a)&&ab(a)},db=function(a,b,c,d,e,f,g){var h,i=Ea();bb(a);var j=function(){if($a[a]){if(h=Ea()-i,h>=d)return ab(a),f(c),void(g&&g());f((c-b)*e(h/d)+b),$a[a].raf=H(j)}};j()},eb={shout:Da,listen:Ca,viewportSize:qa,options:i,isMainScrollAnimating:function(){return fa},getZoomLevel:function(){return s},getCurrentIndex:function(){return m},isDragging:function(){return V},isZooming:function(){return aa},setScrollOffset:function(a,b){sa.x=a,M=sa.y=b,Da("updateScrollOffset",sa)},applyZoomPan:function(a,b,c,d){pa.x=b,pa.y=c,s=a,Ha(d)},init:function(){if(!j&&!k){var c;f.framework=e,f.template=a,f.bg=e.getChildByClass(a,"pswp__bg"),J=a.className,j=!0,N=e.detectFeatures(),H=N.raf,I=N.caf,E=N.transform,L=N.oldIE,f.scrollWrap=e.getChildByClass(a,"pswp__scroll-wrap"),f.container=e.getChildByClass(f.scrollWrap,"pswp__container"),n=f.container.style,f.itemHolders=y=[{el:f.container.children[0],wrap:0,index:-1},{el:f.container.children[1],wrap:0,index:-1},{el:f.container.children[2],wrap:0,index:-1}],y[0].el.style.display=y[2].el.style.display="none",Wa(),r={resize:f.updateSize,orientationchange:function(){clearTimeout(O),O=setTimeout(function(){qa.x!==f.scrollWrap.clientWidth&&f.updateSize()},500)},scroll:Za,keydown:Xa,click:Ya};var d=N.isOldIOSPhone||N.isOldAndroid||N.isMobileOpera;for(N.animationName&&N.transform&&!d||(i.showAnimationDuration=i.hideAnimationDuration=0),c=0;c<wa.length;c++)f["init"+wa[c]]();if(b){var g=f.ui=new b(f,e);g.init()}Da("firstUpdate"),m=m||i.index||0,(isNaN(m)||m<0||m>=ac())&&(m=0),f.currItem=_b(m),(N.isOldIOSPhone||N.isOldAndroid)&&(va=!1),a.setAttribute("aria-hidden","false"),i.modal&&(va?a.style.position="fixed":(a.style.position="absolute",a.style.top=e.getScrollY()+"px")),void 0===M&&(Da("initialLayout"),M=K=e.getScrollY());var l="pswp--open ";for(i.mainClass&&(l+=i.mainClass+" "),i.showHideOpacity&&(l+="pswp--animate_opacity "),l+=G?"pswp--touch":"pswp--notouch",l+=N.animationName?" pswp--css_animation":"",l+=N.svg?" pswp--svg":"",e.addClass(a,l),f.updateSize(),o=-1,ua=null,c=0;c<h;c++)Ja((c+o)*ta.x,y[c].el.style);L||e.bind(f.scrollWrap,q,f),Ca("initialZoomInEnd",function(){f.setContent(y[0],m-1),f.setContent(y[2],m+1),y[0].el.style.display=y[2].el.style.display="block",i.focus&&a.focus(),Qa()}),f.setContent(y[1],m),f.updateCurrItem(),Da("afterInit"),va||(w=setInterval(function(){_a||V||aa||s!==f.currItem.initialZoomLevel||f.updateSize()},1e3)),e.addClass(a,"pswp--visible")}},close:function(){j&&(j=!1,k=!0,Da("close"),Ra(),cc(f.currItem,null,!0,f.destroy))},destroy:function(){Da("destroy"),Xb&&clearTimeout(Xb),a.setAttribute("aria-hidden","true"),a.className=J,w&&clearInterval(w),e.unbind(f.scrollWrap,q,f),e.unbind(window,"scroll",f),zb(),cb(),Ba=null},panTo:function(a,b,c){c||(a>da.min.x?a=da.min.x:a<da.max.x&&(a=da.max.x),b>da.min.y?b=da.min.y:b<da.max.y&&(b=da.max.y)),pa.x=a,pa.y=b,Ha()},handleEvent:function(a){a=a||window.event,r[a.type]&&r[a.type](a)},goTo:function(a){a=Aa(a);var b=a-m;ua=b,m=a,f.currItem=_b(m),ra-=b,Ka(ta.x*ra),cb(),fa=!1,f.updateCurrItem()},next:function(){f.goTo(m+1)},prev:function(){f.goTo(m-1)},updateCurrZoomItem:function(a){if(a&&Da("beforeChange",0),y[1].el.children.length){var b=y[1].el.children[0];ea=e.hasClass(b,"pswp__zoom-wrap")?b.style:null}else ea=null;da=f.currItem.bounds,t=s=f.currItem.initialZoomLevel,pa.x=da.center.x,pa.y=da.center.y,a&&Da("afterChange")},invalidateCurrItems:function(){x=!0;for(var a=0;a<h;a++)y[a].item&&(y[a].item.needsUpdate=!0)},updateCurrItem:function(a){if(0!==ua){var b,c=Math.abs(ua);if(!(a&&c<2)){f.currItem=_b(m),ya=!1,Da("beforeChange",ua),c>=h&&(o+=ua+(ua>0?-h:h),c=h);for(var d=0;d<c;d++)ua>0?(b=y.shift(),y[h-1]=b,o++,Ja((o+2)*ta.x,b.el.style),f.setContent(b,m-c+d+1+1)):(b=y.pop(),y.unshift(b),o--,Ja(o*ta.x,b.el.style),f.setContent(b,m+c-d-1-1));if(ea&&1===Math.abs(ua)){var e=_b(z);e.initialZoomLevel!==s&&(ic(e,qa),mc(e),Ia(e))}ua=0,f.updateCurrZoomItem(),z=m,Da("afterChange")}}},updateSize:function(b){if(!va&&i.modal){var c=e.getScrollY();if(M!==c&&(a.style.top=c+"px",M=c),!b&&xa.x===window.innerWidth&&xa.y===window.innerHeight)return;xa.x=window.innerWidth,xa.y=window.innerHeight,a.style.height=xa.y+"px"}if(qa.x=f.scrollWrap.clientWidth,qa.y=f.scrollWrap.clientHeight,Za(),ta.x=qa.x+Math.round(qa.x*i.spacing),ta.y=qa.y,Ka(ta.x*ra),Da("beforeResize"),void 0!==o){for(var d,g,j,k=0;k<h;k++)d=y[k],Ja((k+o)*ta.x,d.el.style),j=m+k-1,i.loop&&ac()>2&&(j=Aa(j)),g=_b(j),g&&(x||g.needsUpdate||!g.bounds)?(f.cleanSlide(g),f.setContent(d,j),1===k&&(f.currItem=g,f.updateCurrZoomItem(!0)),g.needsUpdate=!1):d.index===-1&&j>=0&&f.setContent(d,j),g&&g.container&&(ic(g,qa),mc(g),Ia(g));x=!1}t=s=f.currItem.initialZoomLevel,da=f.currItem.bounds,da&&(pa.x=da.center.x,pa.y=da.center.y,Ha(!0)),Da("resize")},zoomTo:function(a,b,c,d,f){b&&(t=s,ub.x=Math.abs(b.x)-pa.x,ub.y=Math.abs(b.y)-pa.y,Ma(oa,pa));var g=Sa(a,!1),h={};Va("x",g,h,a),Va("y",g,h,a);var i=s,j={x:pa.x,y:pa.y};Na(h);var k=function(b){1===b?(s=a,pa.x=h.x,pa.y=h.y):(s=(a-i)*b+i,pa.x=(h.x-j.x)*b+j.x,pa.y=(h.y-j.y)*b+j.y),f&&f(b),Ha(1===b)};c?db("customZoomTo",0,1,c,d||e.easing.sine.inOut,k):k(1)}},fb=30,gb=10,hb={},ib={},jb={},kb={},lb={},mb=[],nb={},ob=[],pb={},qb=0,rb=ma(),sb=0,tb=ma(),ub=ma(),vb=ma(),wb=function(a,b){return a.x===b.x&&a.y===b.y},xb=function(a,b){return Math.abs(a.x-b.x)<g&&Math.abs(a.y-b.y)<g},yb=function(a,b){return pb.x=Math.abs(a.x-b.x),pb.y=Math.abs(a.y-b.y),Math.sqrt(pb.x*pb.x+pb.y*pb.y)},zb=function(){Z&&(I(Z),Z=null)},Ab=function(){V&&(Z=H(Ab),Qb())},Bb=function(){return!("fit"===i.scaleMode&&s===f.currItem.initialZoomLevel)},Cb=function(a,b){return!(!a||a===document)&&(!(a.getAttribute("class")&&a.getAttribute("class").indexOf("pswp__scroll-wrap")>-1)&&(b(a)?a:Cb(a.parentNode,b)))},Db={},Eb=function(a,b){return Db.prevent=!Cb(a.target,i.isClickableElement),Da("preventDragEvent",a,b,Db),Db.prevent},Fb=function(a,b){return b.x=a.pageX,b.y=a.pageY,b.id=a.identifier,b},Gb=function(a,b,c){c.x=.5*(a.x+b.x),c.y=.5*(a.y+b.y)},Hb=function(a,b,c){if(a-Q>50){var d=ob.length>2?ob.shift():{};d.x=b,d.y=c,ob.push(d),Q=a}},Ib=function(){var a=pa.y-f.currItem.initialPosition.y;return 1-Math.abs(a/(qa.y/2))},Jb={},Kb={},Lb=[],Mb=function(a){for(;Lb.length>0;)Lb.pop();return F?(la=0,mb.forEach(function(a){0===la?Lb[0]=a:1===la&&(Lb[1]=a),la++})):a.type.indexOf("touch")>-1?a.touches&&a.touches.length>0&&(Lb[0]=Fb(a.touches[0],Jb),a.touches.length>1&&(Lb[1]=Fb(a.touches[1],Kb))):(Jb.x=a.pageX,Jb.y=a.pageY,Jb.id="",Lb[0]=Jb),Lb},Nb=function(a,b){var c,d,e,g,h=0,j=pa[a]+b[a],k=b[a]>0,l=tb.x+b.x,m=tb.x-nb.x;return c=j>da.min[a]||j<da.max[a]?i.panEndFriction:1,j=pa[a]+b[a]*c,!i.allowPanToNext&&s!==f.currItem.initialZoomLevel||(ea?"h"!==ga||"x"!==a||X||(k?(j>da.min[a]&&(c=i.panEndFriction,h=da.min[a]-j,d=da.min[a]-oa[a]),(d<=0||m<0)&&ac()>1?(g=l,m<0&&l>nb.x&&(g=nb.x)):da.min.x!==da.max.x&&(e=j)):(j<da.max[a]&&(c=i.panEndFriction,h=j-da.max[a],d=oa[a]-da.max[a]),(d<=0||m>0)&&ac()>1?(g=l,m>0&&l<nb.x&&(g=nb.x)):da.min.x!==da.max.x&&(e=j))):g=l,"x"!==a)?void(fa||$||s>f.currItem.fitRatio&&(pa[a]+=b[a]*c)):(void 0!==g&&(Ka(g,!0),$=g!==nb.x),da.min.x!==da.max.x&&(void 0!==e?pa.x=e:$||(pa.x+=b.x*c)),void 0!==g)},Ob=function(a){if(!("mousedown"===a.type&&a.button>0)){if($b)return void a.preventDefault();if(!U||"mousedown"!==a.type){if(Eb(a,!0)&&a.preventDefault(),Da("pointerDown"),F){var b=e.arraySearch(mb,a.pointerId,"id");b<0&&(b=mb.length),mb[b]={x:a.pageX,y:a.pageY,id:a.pointerId}}var c=Mb(a),d=c.length;_=null,cb(),V&&1!==d||(V=ha=!0,e.bind(window,p,f),S=ka=ia=T=$=Y=W=X=!1,ga=null,Da("firstTouchStart",c),Ma(oa,pa),na.x=na.y=0,Ma(kb,c[0]),Ma(lb,kb),nb.x=ta.x*ra,ob=[{x:kb.x,y:kb.y}],Q=P=Ea(),Sa(s,!0),zb(),Ab()),!aa&&d>1&&!fa&&!$&&(t=s,X=!1,aa=W=!0,na.y=na.x=0,Ma(oa,pa),Ma(hb,c[0]),Ma(ib,c[1]),Gb(hb,ib,vb),ub.x=Math.abs(vb.x)-pa.x,ub.y=Math.abs(vb.y)-pa.y,ba=ca=yb(hb,ib))}}},Pb=function(a){if(a.preventDefault(),F){var b=e.arraySearch(mb,a.pointerId,"id");if(b>-1){var c=mb[b];c.x=a.pageX,c.y=a.pageY}}if(V){var d=Mb(a);if(ga||Y||aa)_=d;else if(tb.x!==ta.x*ra)ga="h";else{var f=Math.abs(d[0].x-kb.x)-Math.abs(d[0].y-kb.y);Math.abs(f)>=gb&&(ga=f>0?"h":"v",_=d)}}},Qb=function(){if(_){var a=_.length;if(0!==a)if(Ma(hb,_[0]),jb.x=hb.x-kb.x,jb.y=hb.y-kb.y,aa&&a>1){if(kb.x=hb.x,kb.y=hb.y,!jb.x&&!jb.y&&wb(_[1],ib))return;Ma(ib,_[1]),X||(X=!0,Da("zoomGestureStarted"));var b=yb(hb,ib),c=Vb(b);c>f.currItem.initialZoomLevel+f.currItem.initialZoomLevel/15&&(ka=!0);var d=1,e=Ta(),g=Ua();if(c<e)if(i.pinchToClose&&!ka&&t<=f.currItem.initialZoomLevel){var h=e-c,j=1-h/(e/1.2);Fa(j),Da("onPinchClose",j),ia=!0}else d=(e-c)/e,d>1&&(d=1),c=e-d*(e/3);else c>g&&(d=(c-g)/(6*e),d>1&&(d=1),c=g+d*e);d<0&&(d=0),ba=b,Gb(hb,ib,rb),na.x+=rb.x-vb.x,na.y+=rb.y-vb.y,Ma(vb,rb),pa.x=La("x",c),pa.y=La("y",c),S=c>s,s=c,Ha()}else{if(!ga)return;if(ha&&(ha=!1,Math.abs(jb.x)>=gb&&(jb.x-=_[0].x-lb.x),Math.abs(jb.y)>=gb&&(jb.y-=_[0].y-lb.y)),kb.x=hb.x,kb.y=hb.y,0===jb.x&&0===jb.y)return;if("v"===ga&&i.closeOnVerticalDrag&&!Bb()){na.y+=jb.y,pa.y+=jb.y;var k=Ib();return T=!0,Da("onVerticalDrag",k),Fa(k),void Ha()}Hb(Ea(),hb.x,hb.y),Y=!0,da=f.currItem.bounds;var l=Nb("x",jb);l||(Nb("y",jb),Na(pa),Ha())}}},Rb=function(a){if(N.isOldAndroid){if(U&&"mouseup"===a.type)return;a.type.indexOf("touch")>-1&&(clearTimeout(U),U=setTimeout(function(){U=0},600))}Da("pointerUp"),Eb(a,!1)&&a.preventDefault();var b;if(F){var c=e.arraySearch(mb,a.pointerId,"id");if(c>-1)if(b=mb.splice(c,1)[0],navigator.msPointerEnabled){var d={4:"mouse",2:"touch",3:"pen"};b.type=d[a.pointerType],b.type||(b.type=a.pointerType||"mouse")}else b.type=a.pointerType||"mouse"}var g,h=Mb(a),j=h.length;if("mouseup"===a.type&&(j=0),2===j)return _=null,!0;1===j&&Ma(lb,h[0]),0!==j||ga||fa||(b||("mouseup"===a.type?b={x:a.pageX,y:a.pageY,type:"mouse"}:a.changedTouches&&a.changedTouches[0]&&(b={x:a.changedTouches[0].pageX,y:a.changedTouches[0].pageY,type:"touch"})),Da("touchRelease",a,b));var k=-1;if(0===j&&(V=!1,e.unbind(window,p,f),zb(),aa?k=0:sb!==-1&&(k=Ea()-sb)),sb=1===j?Ea():-1,g=k!==-1&&k<150?"zoom":"swipe",aa&&j<2&&(aa=!1,1===j&&(g="zoomPointerUp"),Da("zoomGestureEnded")),_=null,Y||X||fa||T)if(cb(),R||(R=Sb()),R.calculateSwipeSpeed("x"),T){var l=Ib();if(l<i.verticalDragRange)f.close();else{var m=pa.y,n=ja;db("verticalDrag",0,1,300,e.easing.cubic.out,function(a){pa.y=(f.currItem.initialPosition.y-m)*a+m,Fa((1-n)*a+n),Ha()}),Da("onVerticalDrag",1)}}else{if(($||fa)&&0===j){var o=Ub(g,R);if(o)return;g="zoomPointerUp"}if(!fa)return"swipe"!==g?void Wb():void(!$&&s>f.currItem.fitRatio&&Tb(R))}},Sb=function(){var a,b,c={lastFlickOffset:{},lastFlickDist:{},lastFlickSpeed:{},slowDownRatio:{},slowDownRatioReverse:{},speedDecelerationRatio:{},speedDecelerationRatioAbs:{},distanceOffset:{},backAnimDestination:{},backAnimStarted:{},calculateSwipeSpeed:function(d){ob.length>1?(a=Ea()-Q+50,b=ob[ob.length-2][d]):(a=Ea()-P,b=lb[d]),c.lastFlickOffset[d]=kb[d]-b,c.lastFlickDist[d]=Math.abs(c.lastFlickOffset[d]),c.lastFlickDist[d]>20?c.lastFlickSpeed[d]=c.lastFlickOffset[d]/a:c.lastFlickSpeed[d]=0,Math.abs(c.lastFlickSpeed[d])<.1&&(c.lastFlickSpeed[d]=0),c.slowDownRatio[d]=.95,c.slowDownRatioReverse[d]=1-c.slowDownRatio[d],c.speedDecelerationRatio[d]=1},calculateOverBoundsAnimOffset:function(a,b){c.backAnimStarted[a]||(pa[a]>da.min[a]?c.backAnimDestination[a]=da.min[a]:pa[a]<da.max[a]&&(c.backAnimDestination[a]=da.max[a]),void 0!==c.backAnimDestination[a]&&(c.slowDownRatio[a]=.7,c.slowDownRatioReverse[a]=1-c.slowDownRatio[a],c.speedDecelerationRatioAbs[a]<.05&&(c.lastFlickSpeed[a]=0,c.backAnimStarted[a]=!0,db("bounceZoomPan"+a,pa[a],c.backAnimDestination[a],b||300,e.easing.sine.out,function(b){pa[a]=b,Ha()}))))},calculateAnimOffset:function(a){c.backAnimStarted[a]||(c.speedDecelerationRatio[a]=c.speedDecelerationRatio[a]*(c.slowDownRatio[a]+c.slowDownRatioReverse[a]-c.slowDownRatioReverse[a]*c.timeDiff/10),c.speedDecelerationRatioAbs[a]=Math.abs(c.lastFlickSpeed[a]*c.speedDecelerationRatio[a]),c.distanceOffset[a]=c.lastFlickSpeed[a]*c.speedDecelerationRatio[a]*c.timeDiff,pa[a]+=c.distanceOffset[a])},panAnimLoop:function(){if($a.zoomPan&&($a.zoomPan.raf=H(c.panAnimLoop),c.now=Ea(),c.timeDiff=c.now-c.lastNow,c.lastNow=c.now,c.calculateAnimOffset("x"),c.calculateAnimOffset("y"),Ha(),c.calculateOverBoundsAnimOffset("x"),c.calculateOverBoundsAnimOffset("y"),c.speedDecelerationRatioAbs.x<.05&&c.speedDecelerationRatioAbs.y<.05))return pa.x=Math.round(pa.x),pa.y=Math.round(pa.y),Ha(),void ab("zoomPan")}};return c},Tb=function(a){return a.calculateSwipeSpeed("y"),da=f.currItem.bounds,a.backAnimDestination={},a.backAnimStarted={},Math.abs(a.lastFlickSpeed.x)<=.05&&Math.abs(a.lastFlickSpeed.y)<=.05?(a.speedDecelerationRatioAbs.x=a.speedDecelerationRatioAbs.y=0,a.calculateOverBoundsAnimOffset("x"),a.calculateOverBoundsAnimOffset("y"),!0):(bb("zoomPan"),a.lastNow=Ea(),void a.panAnimLoop())},Ub=function(a,b){var c;fa||(qb=m);var d;if("swipe"===a){var g=kb.x-lb.x,h=b.lastFlickDist.x<10;g>fb&&(h||b.lastFlickOffset.x>20)?d=-1:g<-fb&&(h||b.lastFlickOffset.x<-20)&&(d=1)}var j;d&&(m+=d,m<0?(m=i.loop?ac()-1:0,j=!0):m>=ac()&&(m=i.loop?0:ac()-1,j=!0),j&&!i.loop||(ua+=d,ra-=d,c=!0));var k,l=ta.x*ra,n=Math.abs(l-tb.x);return c||l>tb.x==b.lastFlickSpeed.x>0?(k=Math.abs(b.lastFlickSpeed.x)>0?n/Math.abs(b.lastFlickSpeed.x):333,k=Math.min(k,400),k=Math.max(k,250)):k=333,qb===m&&(c=!1),fa=!0,Da("mainScrollAnimStart"),db("mainScroll",tb.x,l,k,e.easing.cubic.out,Ka,function(){cb(),fa=!1,qb=-1,(c||qb!==m)&&f.updateCurrItem(),Da("mainScrollAnimComplete")}),c&&f.updateCurrItem(!0),c},Vb=function(a){return 1/ca*a*t},Wb=function(){var a=s,b=Ta(),c=Ua();s<b?a=b:s>c&&(a=c);var d,g=1,h=ja;return ia&&!S&&!ka&&s<b?(f.close(),!0):(ia&&(d=function(a){Fa((g-h)*a+h)}),f.zoomTo(a,0,200,e.easing.cubic.out,d),!0)};za("Gestures",{publicMethods:{initGestures:function(){var a=function(a,b,c,d,e){A=a+b,B=a+c,C=a+d,D=e?a+e:""};F=N.pointerEvent,F&&N.touch&&(N.touch=!1),F?navigator.msPointerEnabled?a("MSPointer","Down","Move","Up","Cancel"):a("pointer","down","move","up","cancel"):N.touch?(a("touch","start","move","end","cancel"),G=!0):a("mouse","down","move","up"),p=B+" "+C+" "+D,q=A,F&&!G&&(G=navigator.maxTouchPoints>1||navigator.msMaxTouchPoints>1),f.likelyTouchDevice=G,r[A]=Ob,r[B]=Pb,r[C]=Rb,D&&(r[D]=r[C]),N.touch&&(q+=" mousedown",p+=" mousemove mouseup",r.mousedown=r[A],r.mousemove=r[B],r.mouseup=r[C]),G||(i.allowPanToNext=!1)}}});var Xb,Yb,Zb,$b,_b,ac,bc,cc=function(b,c,d,g){Xb&&clearTimeout(Xb),$b=!0,Zb=!0;var h;b.initialLayout?(h=b.initialLayout,b.initialLayout=null):h=i.getThumbBoundsFn&&i.getThumbBoundsFn(m);var j=d?i.hideAnimationDuration:i.showAnimationDuration,k=function(){ab("initialZoom"),d?(f.template.removeAttribute("style"),f.bg.removeAttribute("style")):(Fa(1),c&&(c.style.display="block"),e.addClass(a,"pswp--animated-in"),Da("initialZoom"+(d?"OutEnd":"InEnd"))),g&&g(),$b=!1};if(!j||!h||void 0===h.x)return Da("initialZoom"+(d?"Out":"In")),s=b.initialZoomLevel,Ma(pa,b.initialPosition),Ha(),a.style.opacity=d?0:1,Fa(1),void(j?setTimeout(function(){k()},j):k());var n=function(){var c=l,g=!f.currItem.src||f.currItem.loadError||i.showHideOpacity;b.miniImg&&(b.miniImg.style.webkitBackfaceVisibility="hidden"),d||(s=h.w/b.w,pa.x=h.x,pa.y=h.y-K,f[g?"template":"bg"].style.opacity=.001,Ha()),bb("initialZoom"),d&&!c&&e.removeClass(a,"pswp--animated-in"),g&&(d?e[(c?"remove":"add")+"Class"](a,"pswp--animate_opacity"):setTimeout(function(){e.addClass(a,"pswp--animate_opacity")},30)),Xb=setTimeout(function(){if(Da("initialZoom"+(d?"Out":"In")),d){var f=h.w/b.w,i={x:pa.x,y:pa.y},l=s,m=ja,n=function(b){1===b?(s=f,pa.x=h.x,pa.y=h.y-M):(s=(f-l)*b+l,pa.x=(h.x-i.x)*b+i.x,pa.y=(h.y-M-i.y)*b+i.y),Ha(),g?a.style.opacity=1-b:Fa(m-b*m)};c?db("initialZoom",0,1,j,e.easing.cubic.out,n,k):(n(1),Xb=setTimeout(k,j+20))}else s=b.initialZoomLevel,Ma(pa,b.initialPosition),Ha(),Fa(1),g?a.style.opacity=1:Fa(1),Xb=setTimeout(k,j+20)},d?25:90)};n()},dc={},ec=[],fc={index:0,errorMsg:'<div class="pswp__error-msg"><a href="%url%" target="_blank">The image</a> could not be loaded.</div>',forceProgressiveLoading:!1,preload:[1,1],getNumItemsFn:function(){return Yb.length}},gc=function(){return{center:{x:0,y:0},max:{x:0,y:0},min:{x:0,y:0}}},hc=function(a,b,c){var d=a.bounds;d.center.x=Math.round((dc.x-b)/2),d.center.y=Math.round((dc.y-c)/2)+a.vGap.top,d.max.x=b>dc.x?Math.round(dc.x-b):d.center.x,d.max.y=c>dc.y?Math.round(dc.y-c)+a.vGap.top:d.center.y,d.min.x=b>dc.x?0:d.center.x,d.min.y=c>dc.y?a.vGap.top:d.center.y},ic=function(a,b,c){if(a.src&&!a.loadError){var d=!c;if(d&&(a.vGap||(a.vGap={top:0,bottom:0}),Da("parseVerticalMargin",a)),dc.x=b.x,dc.y=b.y-a.vGap.top-a.vGap.bottom,d){var e=dc.x/a.w,f=dc.y/a.h;a.fitRatio=e<f?e:f;var g=i.scaleMode;"orig"===g?c=1:"fit"===g&&(c=a.fitRatio),c>1&&(c=1),a.initialZoomLevel=c,a.bounds||(a.bounds=gc())}if(!c)return;return hc(a,a.w*c,a.h*c),d&&c===a.initialZoomLevel&&(a.initialPosition=a.bounds.center),a.bounds}return a.w=a.h=0,a.initialZoomLevel=a.fitRatio=1,a.bounds=gc(),a.initialPosition=a.bounds.center,a.bounds},jc=function(a,b,c,d,e,g){b.loadError||d&&(b.imageAppended=!0,mc(b,d,b===f.currItem&&ya),c.appendChild(d),g&&setTimeout(function(){b&&b.loaded&&b.placeholder&&(b.placeholder.style.display="none",b.placeholder=null)},500))},kc=function(a){a.loading=!0,a.loaded=!1;var b=a.img=e.createEl("pswp__img","img"),c=function(){a.loading=!1,a.loaded=!0,a.loadComplete?a.loadComplete(a):a.img=null,b.onload=b.onerror=null,b=null};return b.onload=c,b.onerror=function(){a.loadError=!0,c()},b.src=a.src,b},lc=function(a,b){if(a.src&&a.loadError&&a.container)return b&&(a.container.innerHTML=""),a.container.innerHTML=i.errorMsg.replace("%url%",a.src),!0},mc=function(a,b,c){if(a.src){b||(b=a.container.lastChild);var d=c?a.w:Math.round(a.w*a.fitRatio),e=c?a.h:Math.round(a.h*a.fitRatio);a.placeholder&&!a.loaded&&(a.placeholder.style.width=d+"px",a.placeholder.style.height=e+"px"),b.style.width=d+"px",b.style.height=e+"px"}},nc=function(){if(ec.length){for(var a,b=0;b<ec.length;b++)a=ec[b],a.holder.index===a.index&&jc(a.index,a.item,a.baseDiv,a.img,!1,a.clearPlaceholder);ec=[]}};za("Controller",{publicMethods:{lazyLoadItem:function(a){a=Aa(a);var b=_b(a);b&&(!b.loaded&&!b.loading||x)&&(Da("gettingData",a,b),b.src&&kc(b))},initController:function(){e.extend(i,fc,!0),f.items=Yb=c,_b=f.getItemAt,ac=i.getNumItemsFn,bc=i.loop,ac()<3&&(i.loop=!1),Ca("beforeChange",function(a){var b,c=i.preload,d=null===a||a>=0,e=Math.min(c[0],ac()),g=Math.min(c[1],ac());for(b=1;b<=(d?g:e);b++)f.lazyLoadItem(m+b);for(b=1;b<=(d?e:g);b++)f.lazyLoadItem(m-b)}),Ca("initialLayout",function(){f.currItem.initialLayout=i.getThumbBoundsFn&&i.getThumbBoundsFn(m)}),Ca("mainScrollAnimComplete",nc),Ca("initialZoomInEnd",nc),Ca("destroy",function(){for(var a,b=0;b<Yb.length;b++)a=Yb[b],a.container&&(a.container=null),a.placeholder&&(a.placeholder=null),a.img&&(a.img=null),a.preloader&&(a.preloader=null),a.loadError&&(a.loaded=a.loadError=!1);ec=null})},getItemAt:function(a){return a>=0&&(void 0!==Yb[a]&&Yb[a])},allowProgressiveImg:function(){return i.forceProgressiveLoading||!G||i.mouseUsed||screen.width>1200},setContent:function(a,b){i.loop&&(b=Aa(b));var c=f.getItemAt(a.index);c&&(c.container=null);var d,g=f.getItemAt(b);if(!g)return void(a.el.innerHTML="");Da("gettingData",b,g),a.index=b,a.item=g;var h=g.container=e.createEl("pswp__zoom-wrap");if(!g.src&&g.html&&(g.html.tagName?h.appendChild(g.html):h.innerHTML=g.html),lc(g),ic(g,qa),!g.src||g.loadError||g.loaded)g.src&&!g.loadError&&(d=e.createEl("pswp__img","img"),d.style.opacity=1,d.src=g.src,mc(g,d),jc(b,g,h,d,!0));else{if(g.loadComplete=function(c){if(j){if(a&&a.index===b){if(lc(c,!0))return c.loadComplete=c.img=null,ic(c,qa),Ia(c),void(a.index===m&&f.updateCurrZoomItem());c.imageAppended?!$b&&c.placeholder&&(c.placeholder.style.display="none",c.placeholder=null):N.transform&&(fa||$b)?ec.push({item:c,baseDiv:h,img:c.img,index:b,holder:a,clearPlaceholder:!0}):jc(b,c,h,c.img,fa||$b,!0)}c.loadComplete=null,c.img=null,Da("imageLoadComplete",b,c)}},e.features.transform){var k="pswp__img pswp__img--placeholder";k+=g.msrc?"":" pswp__img--placeholder--blank";var l=e.createEl(k,g.msrc?"img":"");g.msrc&&(l.src=g.msrc),mc(g,l),h.appendChild(l),g.placeholder=l}g.loading||kc(g),f.allowProgressiveImg()&&(!Zb&&N.transform?ec.push({item:g,baseDiv:h,img:g.img,index:b,holder:a}):jc(b,g,h,g.img,!0,!0))}Zb||b!==m?Ia(g):(ea=h.style,cc(g,d||g.img)),a.el.innerHTML="",a.el.appendChild(h)},cleanSlide:function(a){a.img&&(a.img.onload=a.img.onerror=null),a.loaded=a.loading=a.img=a.imageAppended=!1}}});var oc,pc={},qc=function(a,b,c){var d=document.createEvent("CustomEvent"),e={origEvent:a,target:a.target,releasePoint:b,pointerType:c||"touch"};d.initCustomEvent("pswpTap",!0,!0,e),a.target.dispatchEvent(d)};za("Tap",{publicMethods:{initTap:function(){Ca("firstTouchStart",f.onTapStart),Ca("touchRelease",f.onTapRelease),Ca("destroy",function(){pc={},oc=null})},onTapStart:function(a){a.length>1&&(clearTimeout(oc),oc=null)},onTapRelease:function(a,b){if(b&&!Y&&!W&&!_a){var c=b;if(oc&&(clearTimeout(oc),oc=null,xb(c,pc)))return void Da("doubleTap",c);if("mouse"===b.type)return void qc(a,b,"mouse");var d=a.target.tagName.toUpperCase();if("BUTTON"===d||e.hasClass(a.target,"pswp__single-tap"))return void qc(a,b);Ma(pc,c),oc=setTimeout(function(){qc(a,b),oc=null},300)}}}});var rc;za("DesktopZoom",{publicMethods:{initDesktopZoom:function(){L||(G?Ca("mouseUsed",function(){f.setupDesktopZoom()}):f.setupDesktopZoom(!0))},setupDesktopZoom:function(b){rc={};var c="wheel mousewheel DOMMouseScroll";Ca("bindEvents",function(){e.bind(a,c,f.handleMouseWheel)}),Ca("unbindEvents",function(){rc&&e.unbind(a,c,f.handleMouseWheel)}),f.mouseZoomedIn=!1;var d,g=function(){f.mouseZoomedIn&&(e.removeClass(a,"pswp--zoomed-in"),f.mouseZoomedIn=!1),s<1?e.addClass(a,"pswp--zoom-allowed"):e.removeClass(a,"pswp--zoom-allowed"),h()},h=function(){d&&(e.removeClass(a,"pswp--dragging"),d=!1)};Ca("resize",g),Ca("afterChange",g),Ca("pointerDown",function(){f.mouseZoomedIn&&(d=!0,e.addClass(a,"pswp--dragging"))}),Ca("pointerUp",h),b||g()},handleMouseWheel:function(a){if(s<=f.currItem.fitRatio)return i.modal&&(!i.closeOnScroll||_a||V?a.preventDefault():E&&Math.abs(a.deltaY)>2&&(l=!0,f.close())),!0;if(a.stopPropagation(),rc.x=0,"deltaX"in a)1===a.deltaMode?(rc.x=18*a.deltaX,rc.y=18*a.deltaY):(rc.x=a.deltaX,rc.y=a.deltaY);else if("wheelDelta"in a)a.wheelDeltaX&&(rc.x=-.16*a.wheelDeltaX),a.wheelDeltaY?rc.y=-.16*a.wheelDeltaY:rc.y=-.16*a.wheelDelta;else{if(!("detail"in a))return;rc.y=a.detail}Sa(s,!0);var b=pa.x-rc.x,c=pa.y-rc.y;(i.modal||b<=da.min.x&&b>=da.max.x&&c<=da.min.y&&c>=da.max.y)&&a.preventDefault(),f.panTo(b,c)},toggleDesktopZoom:function(b){b=b||{x:qa.x/2+sa.x,y:qa.y/2+sa.y};var c=i.getDoubleTapZoom(!0,f.currItem),d=s===c;f.mouseZoomedIn=!d,f.zoomTo(d?f.currItem.initialZoomLevel:c,b,333),e[(d?"remove":"add")+"Class"](a,"pswp--zoomed-in")}}});var sc,tc,uc,vc,wc,xc,yc,zc,Ac,Bc,Cc,Dc,Ec={history:!0,galleryUID:1},Fc=function(){return Cc.hash.substring(1)},Gc=function(){sc&&clearTimeout(sc),uc&&clearTimeout(uc)},Hc=function(){var a=Fc(),b={};if(a.length<5)return b;var c,d=a.split("&");for(c=0;c<d.length;c++)if(d[c]){var e=d[c].split("=");e.length<2||(b[e[0]]=e[1])}if(i.galleryPIDs){var f=b.pid;for(b.pid=0,c=0;c<Yb.length;c++)if(Yb[c].pid===f){b.pid=c;break}}else b.pid=parseInt(b.pid,10)-1;return b.pid<0&&(b.pid=0),b},Ic=function(){if(uc&&clearTimeout(uc),_a||V)return void(uc=setTimeout(Ic,500));vc?clearTimeout(tc):vc=!0;var a=m+1,b=_b(m);b.hasOwnProperty("pid")&&(a=b.pid);var c=yc+"&gid="+i.galleryUID+"&pid="+a;zc||Cc.hash.indexOf(c)===-1&&(Bc=!0);var d=Cc.href.split("#")[0]+"#"+c;Dc?"#"+c!==window.location.hash&&history[zc?"replaceState":"pushState"]("",document.title,d):zc?Cc.replace(d):Cc.hash=c,zc=!0,tc=setTimeout(function(){vc=!1},60)};za("History",{publicMethods:{initHistory:function(){if(e.extend(i,Ec,!0),i.history){Cc=window.location,Bc=!1,Ac=!1,zc=!1,yc=Fc(),Dc="pushState"in history,yc.indexOf("gid=")>-1&&(yc=yc.split("&gid=")[0],yc=yc.split("?gid=")[0]),Ca("afterChange",f.updateURL),Ca("unbindEvents",function(){e.unbind(window,"hashchange",f.onHashChange)});var a=function(){xc=!0,Ac||(Bc?history.back():yc?Cc.hash=yc:Dc?history.pushState("",document.title,Cc.pathname+Cc.search):Cc.hash=""),Gc()};Ca("unbindEvents",function(){l&&a()}),Ca("destroy",function(){xc||a()}),Ca("firstUpdate",function(){m=Hc().pid});var b=yc.indexOf("pid=");b>-1&&(yc=yc.substring(0,b),"&"===yc.slice(-1)&&(yc=yc.slice(0,-1))),setTimeout(function(){j&&e.bind(window,"hashchange",f.onHashChange)},40)}},onHashChange:function(){return Fc()===yc?(Ac=!0,void f.close()):void(vc||(wc=!0,f.goTo(Hc().pid),wc=!1))},updateURL:function(){Gc(),wc||(zc?sc=setTimeout(Ic,800):Ic())}}}),e.extend(f,eb)};return a});
\ No newline at end of file
index a65e027..0e19f92 100644 (file)
       "password" => "reLH2MgvEsnWNHcy",
     ),
   );
-
-  function getparams(){
-    $get_arr = array();
-    foreach (explode("&", $_SERVER['QUERY_STRING']) as $tmp_arr_param) {
-      $split_param = explode("=", $tmp_arr_param);
-      $get_arr[$split_param[0]] = urldecode($split_param[1]);
-    }
-    return $get_arr; 
-  }
 ?>
\ No newline at end of file
index ff0518b..80fedb6 100644 (file)
@@ -2,6 +2,7 @@
 class database {
   private $conn;
   private $dbconf;
+  private $debug=1;
   public function __construct($pdbconf){
     $this->dbconf = $pdbconf;
     try {
@@ -10,7 +11,7 @@ class database {
       //pgsql:host=localhost;port=5432;dbname=testdb;user=bruce;password=mypass
       $this->conn = new PDO($this->dbconf["type"].':host='.$this->dbconf["host"].';port=5432;dbname='.$this->dbconf["dbname"],$this->dbconf["user"],$this->dbconf["password"]);
     } catch(PDOException $e){
-      fwrite(STDERR, "Connectio Error: ".$e->getMessage()."\n");
+      error_log("Connection Error: ".$e->getMessage());
     }
   }
   
@@ -25,38 +26,71 @@ class database {
   }
 
   public function query($sql){
+    $result = array();
+    if ($this->debug == 1){
+      error_log("QUERY:".$sql);
+    } 
     try {
       if ($this->conn){
         return $this->conn->query($sql);
       }
     } catch(PDOException $e){
-      fwrite(STDERR, "QUERY ERROR: ".$sql."\n");
+      error_log("QUERY ERROR: ".$sql);
     }
-    return null;
+    return $result;
+  }
+
+  public function querybykey($key,$sql){
+    $result = null;
+    $kresult = array();
+    if ($this->debug == 1){
+      error_log("QUERYbykey: ".$sql);
+    }
+    try {
+      if ($this->conn){
+                         $sth = $this->conn->prepare($sql);
+                         $sth->execute();
+        $result = $sth->fetchAll(PDO::FETCH_ASSOC);
+        foreach($result as $r){
+          $kresult[$r[$key]] = $r;
+        }
+         
+        return $kresult;
+      }
+    } catch (PDOException $e){
+      error_log("QUERYBYKEY ERROR: ".$sql);
+    }
+               return $result;
   }
 
   public function queryarray($sql){
     $result = null;
+    if ($this->debug == 1){
+      error_log("QUERYARRAY: ".$sql);
+    }
     try {
       if ($this->conn){
-                         $sth = $this->link->prepare($sql);
+                         $sth = $this->conn->prepare($sql);
                          $sth->execute();
         $result = $sth->fetchAll(PDO::FETCH_ASSOC);
         return $result;
       }
     } catch (PDOException $e){
-      fwrite(STDERR, "QUERYARRAY ERROR: ".$sql."\n");
+      error_log("QUERYARRAY ERROR: ".$sql);
     }
                return $result;
   }
 
   public function exec($sql){
+    if ($this->debug == 1){
+      error_log("EXEC: ".$sql);
+    }
     try {
       if ($this->conn){
         return  $this->exec($sql);
       }
     } catch (PDOException $e){
-      fwrite(STDERR, "EXEC ERROR: ".$sql."\n");
+      error_log("EXEC ERROR: ".$sql);
     }
                return -1;
   }
diff --git a/website/lib/modules/RendezVous.php b/website/lib/modules/RendezVous.php
new file mode 100644 (file)
index 0000000..57bce1f
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+  class rendezvous {
+    private $dbh;
+    public function __construct($dblink){
+      $this->dbh = $dblink;
+    }
+
+    public function getServices(){
+      return $this->dbh->queryarray("select * from rendezvousproducts;");
+    }
+
+    public function getRDVDates(){
+      $startdate = date("Y-m-d");
+      $rdvsql = "select * from defaultdata where id LIKE 'rendezvous_%';";
+      $rdvcfg = $this->dbh->querybykey('id',$rdvsql);
+      //error_log(print_r($rdvcfg,TRUE));
+      $sql = "select ad.weekday,ad.daydates,maxrdv.maxrdv::int4,coalesce(rdv.rdvcnt,0) as rdvcnt from (
+        select date_part('isodow',dd)::integer as weekday,date(dd) as daydates from generate_series(date('".$startdate."')::timestamp,(date('".$startdate."') + interval '".$rdvcfg["rendezvous_maxdateinterval"]["pref"]."')::timestamp,interval '1 day') dd
+      ) ad 
+      left join (
+        select weekday,((date_part('hours',endtime-starttime) * 60) + date_part('minutes',endtime-starttime)) / ((date_part('hours',timesteps) * 60) + date_part('minutes',timesteps)) as maxrdv from rendezvoushours where daydate is null
+      ) maxrdv on ad.weekday=maxrdv.weekday
+      left join (
+        select daydate,count(daydate) as rdvcnt from rendezvous where daydate between date('".$startdate."') and (date('".$startdate."') + interval '".$rdvcfg["rendezvous_maxdateinterval"]["pref"]."')::date and iscanceled is null group by daydate
+      ) rdv on ad.daydates=rdv.daydate
+      where ad.weekday in (select weekday from rendezvoushours where daydate is null)
+      and daydates not in (select daydate as daydates from rendezvoushours where daydate between date('".$startdate."') and (date('".$startdate."') + interval '".$rdvcfg["rendezvous_maxdateinterval"]["pref"]."')::date and starttime is null and endtime is null)
+      and (rdv.rdvcnt is null or rdv.rdvcnt<maxrdv.maxrdv);";
+      return $this->dbh->queryarray($sql);
+    }
+
+    public function getRDVDayFreeTimes($rdvdate){
+      $sql = "select rdvtime from (
+        select date(rdvtime) as daydate,to_char(rdvtime,'HH24:MI') as rdvtime from (
+        select generate_series(date('".$rdvdate."') + starttime::interval,date('".$rdvdate."') + endtime::interval,timesteps)  as rdvtime
+       from rendezvoushours where date_part('isodow',date('".$rdvdate."')) = weekday) altm ) tm
+        left join (select daydate,to_char(timestart,'HH24:MI') as timestart from rendezvous where daydate=date('".$rdvdate."') and isconfirmed=true) rdv on (tm.daydate=rdv.daydate and rdv.timestart=tm.rdvtime)
+        where timestart is null";
+        return $this->dbh->queryarray($sql);
+    }
+
+    public function __destruct(){
+      $this->dbh = null;
+    }
+  }
+?>
\ No newline at end of file
diff --git a/website/manifest.webmanifest b/website/manifest.webmanifest
new file mode 100644 (file)
index 0000000..4ec72c1
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "background_color": "#c42027",
+  "description": "OldBell - Marc den Barbir - Haircuts & Shaves For Men",
+  "display": "fullscreen",
+  "icons": [
+    {
+      "src": "img/appicon.png",
+      "sizes": "192x192",
+      "type": "image/png"
+    }
+  ],
+  "name": "OldBell - Marc den Barbir",
+  "short_name": "OldBell",
+  "start_url": "/oldbell_lu/index.html"
+}
\ No newline at end of file
index db185eb..1621808 100644 (file)
 <link rel="icon" type="image/png" sizes="32x32" href="img/favicon/favicon-32x32.png">
 <link rel="icon" type="image/png" sizes="96x96" href="img/favicon/favicon-96x96.png">
 <link rel="icon" type="image/png" sizes="16x16" href="img/favicon/favicon-16x16.png">
-<link rel="manifest" href="img/favicon/manifest.json">
-<meta name="msapplication-TileColor" content="#ffffff">
+<link rel="manifest" href="manifest.webmanifest">
+<meta name="msapplication-TileColor" content="#c42027">
 <meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
-<meta name="theme-color" content="#ffffff">
+<meta name="theme-color" content="#c42027">
 <link rel="stylesheet" href="js/vendor/tabulator/css/tabulator_site.css?v=1" />
-    <link  rel="stylesheet" href="css/icons.css?v=1">
-    <link  rel="stylesheet" href="css/theme.css?v=1">
-    <link  rel="stylesheet" href="css/style.css?v=1">
+<link rel="stylesheet" href="js/vendor/photoswipe/photoswipe.css?v=1" />
+<link  rel="stylesheet" href="css/icons.css?v=1">
+<link  rel="stylesheet" href="css/theme.css?v=1">
   </head>
   <body class="fullscreen">
-    <div class="display-container" style="width: 100vw; height: 100vh;">
+    <div class="display-container" style="width: 100vw; overflow-y: hidden;">
       {{pagedata}}
     </div> 
-    <script src="js/vendor/tabulator/js/tabulator.js"></script>
-  
-  <script src="js/site.js"></script>
+  <script src="js/vendor/tabulator/js/tabulator.js" type="text/javascript"></script>
+  <script src="js/vendor/moment/moment-with-locales.min.js" type="text/javascript"></script>
+  <script src="js/vendor/photoswipe/photoswipe.min.js" type="text/javascript"></script>
+  <script src="js/myapp.js" type="text/javascript"></script>
+  <script src="js/request.js" type="text/javascript"></script>
 </body>
 </html>
index 35ee171..1ee9acb 100644 (file)
@@ -1,6 +1,6 @@
 <div class="top">
       <div class="bar red-gold">
-        <a class="bar-item button red-gold" href="home.html"><span class="icon-arrow-left2" style="font-size: 30px;"></span><br/>retour</a>
+        <a class="bar-item button red-gold" href="index.html"><span class="icon-arrow-left2" style="font-size: 30px;"></span><br/>retour</a>
         <div class="bar-item">
           <span class="large text-white">Gname{{ galleriename }}</span><br/>
           <span>Gallerie</span>
@@ -69,4 +69,5 @@
     <label class="label">Gallery</label></a>
   </div>
 </div>
-</div>
\ No newline at end of file
+</div>
+<script src="js/gallery.js" type="text/javascript"></script>
\ No newline at end of file
similarity index 85%
rename from website/tmpl/pages/home.html.mustache
rename to website/tmpl/pages/index.html.mustache
index 64594bf..dcb8183 100644 (file)
@@ -1,9 +1,9 @@
 
   <div class="top">
-      <div class="bar red-gold">
+      <div class="bar red-white">
         <img class="bar-item" src="img/logo1.png" style="height: 70px;" alt="logo oldbell" />
         <div class="bar-item" style="color: #fff; font-variant: small-caps; padding: 8px 8px;">Marc de Barbir<br/>Haircuts & Shaves<br/>For Men </div>
-        <a class="bar-item right" style="padding-top: 15px;"><span class="icon-facebook2" style="font-size: 40px;color: #b0834c;"></span></a>
+        <a class="bar-item right" style="padding-top: 15px;"><span class="icon-facebook2" style="font-size: 40px;"></span></a>
       </div>
     </div>
     <div class="display-container" style="margin-top: 70px; height: calc(100vh - 70px); overflow-y: scroll;">
       </div>
 </div>
       <div class="bottom">
-      <div class="bar red-gold">
-        <div class="dropup">
-          <button class="button red-gold" onclick="dropup('dru_address');">
-            <span class="icon-map2" style="font-size: 40px;"></span><br/>Salon
-          </button>
-          <div id="dru_address" class="dropup-content bar-block card red-gold">
-            <a href="#" class="bar-item button">34, rue du Fossé<br/>4123 Esch/Alzette</a>
-            <a class="bar-item button" href="tel:0035226532264">Tel: +352 2653 2264</a>
-          </div>
-        </div>
+      <div class="bar red-white">
+        <a class="bar-item add-button">installer App</button>
+        <a class="bar-item button"><span class="icon-map2" style="font-size: 40px;"></span><br/>Salon</a>
       </div>
     </div>
-  
+
+<script src="js/index.js" type="text/javascript"></script>
index 7371eea..933c51c 100644 (file)
-<div class="top">
-      <div class="bar red-gold">
-        <a class="bar-item button red-gold" href="home.html"><span class="icon-arrow-left2" style="font-size: 30px;"></span><br/>retour</a>
-        <div class="bar-item">
-          <span class="xlarge text-white">Rendez-vous</span>
-        </div>
+<div class="display-container">
+  <div class="panel" id="pnl_rdvservice">
+    <div class="bar red-white">
+      <a class="bar-item button red-white" href="index.html">
+        <span class="icon-home" style="font-size: 30px;"></span><br/>Home
+      </a>
+      <div class="bar-item">
+        <span class="large text-white">Choix du Service</span><br/><span>Rendez-Vous</span>
       </div>
+      <button class="bar-item button right" onclick="rendezvous.viewDates();">
+        <span class="icon-arrow-right" style="font-size: 30px;"></span><br/>Date
+      </button>
     </div>
-    
-    <div class="display-container" style="margin-top: 66px; margin-bottom: 70px; width: 100%!important;">
-      <div class="panel" id="pnl_products">
-        <div class="bar red-gold" style="border-top: 1px solid black;">
-          <div class="bar-item">Choix du Produit / Service</div>
-        </div>
+    <div id="tbl_rdvservice"></div>
+  </div>
+  <!-- Next Step-->
+  <div class="panel" id="pnl_rdvdate" style="display:none;">
+    <div class="bar red-white">
+      <button class="bar-item button" onclick="rendezvous.viewServices();">
+        <span class="icon-arrow-left" style="font-size: 30px;"></span><br/>Services
+      </button>
+      <div class="bar-item">
+        <span class="large text-white">Choix de la Date</span><br/><span>Rendez-Vous</span>
+      </div>
+      <button class="bar-item button right" onclick="rendezvous.viewHours();">
+        <span class="icon-arrow-right" style="font-size: 30px;"></span><br/>Heure
+      </button>
+    </div>
+    <div id="tbl_rdvdate"></div>
+  </div>
+  <!-- Next Step -->
+  <div class="panel" id="pnl_rdvtime"  style="display:none;">
+    <div class="bar red-white">
+      <button class="bar-item button" onclick="rendezvous.viewDates();">
+        <span class="icon-arrow-left" style="font-size: 30px;"></span><br/>Date
+      </button>
+      <div class="bar-item">
+        <span class="large text-white">Choix de l'heure</span><br/><span>Rendez-Vous</span>
       </div>
-      <!--<div class="panel" id="pnl_dates">
-        
+      <button class="bar-item button right" onclick="rendezvous.viewClientData();">
+        <span class="icon-arrow-right" style="font-size: 30px;"></span><br/>Coordonnées
+      </button>
+    </div>
+    <div id="tbl_rdvtime"></div>
+  </div>
+  <!-- Next Step -->
+  <div class="panel" id="pnl_rdvclientdata" style="display:none;">
+    <div class="bar red-white">
+      <button class="bar-item button" onclick="rendezvous.viewHours();">
+        <span class="icon-arrow-left" style="font-size: 30px;"></span><br/>Heure
+      </button>
+      <div class="bar-item">
+        <span class="large text-white">Vos Coordonnées</span><br/><span>Rendez-Vous</span>
       </div>
-      <div class="panel" id="pnl_hours">
-        
+      <button class="bar-item button right" onclick="rendezvous.viewConfirmation();">
+        <span class="icon-arrow-right" style="font-size: 30px;"></span><br/>Confirmation
+      </button>
+    </div>
+    <div class="container">
+      <form id="frm_rdvdata">
+        <input type="hidden" class="data_rendezvous" id="rendezvous_id" data-id=""  data-table="rendezvous" data-column="id" />
+        <div class="container">
+          <label class="label">Nom</label>
+          <input class="input data_rendezvous" type="text" id="rendezvous_clientname" data-id=""  data-table="rendezvous" data-column="clientname"/>
+        </div>
+        <div class="container">
+          <label class="label">Téléphone</label>
+          <input class="input data_rendezvous" type="text" id="rendezvous_clientphone" data-id=""  data-table="rendezvous" data-column="clientphone"/>
+        </div>
+        <div class="container">
+          <label class="label">E-Mail</label>
+          <input class="input data_rendezvous" type="text" id="rendezvous_clientemail" data-id=""  data-table="rendezvous" data-column="clientemail"/>
+        </div>
+        <div class="container">
+          <label class="label">Message</label>
+          <textarea class="input data_rendezvous" style="width: 100%; height: 100px;" id="rendezvous_clientmessage" data-id=""  data-table="rendezvous" data-column="clientmessage"></textarea>
+        </div>
+      </form>
+    </div>
+  </div>
+  <!-- Next Step -->
+  <div class="panel" id="pnl_rdvconfirm"  style="display:none;">
+    <div class="bar red-white">
+      <button class="bar-item button" onclick="rendezvous.viewClientData();">
+        <span class="icon-arrow-left" style="font-size: 30px;"></span><br/>Coordonnées
+      </button>
+      <div class="bar-item">
+        <span class="large text-white">Confirmation</span><br/><span>Rendez-Vous</span>
       </div>
-      <div class="panel" id="pnl_data">
-        <form>
-          <div class="row">
+    </div>
+    <div class="container">
+      <fieldset>
+        <legend>Détails Rendez-Vous</legend>
+        <form id="frm_rdvconfirm">
+        <input type="hidden" class="data_rdvconfirm" id="rendezvous_id" data-id=""  data-table="rendezvous" data-column="id" />
+        <div class="row">
+          <div class="container">
+            <label class="label">Service</label>
+            <input class="input data_rdvconfirm readonly" type="text" id="rdvconfirm_product" data-id=""  data-table="rendezvous" data-column="product" readonly/>
+          </div>
+          <div class="container">
+            <label class="label">Date</label>
+            <input class="input data_rdvconfirm readonly" type="text" id="rdvconfirm_daydate" data-id=""  data-table="rendezvous" data-column="daydate" readonly/>
+          </div>
+          <div class="container">
+            <label class="label">Heure</label>
+            <input class="input data_rdvconfirm readonly" type="text" id="rdvconfirm_starttime" data-id=""  data-table="rendezvous" data-column="starttime" readonly/>
+          </div>
           <div class="container">
             <label class="label">Nom</label>
-            <input class="input data_client" type="text" id="client_name" name="client_name" />
+            <input class="input data_rdvconfirm readonly" type="text" id="rdvconfirm_clientname" data-id=""  data-table="rendezvous" data-column="clientname" readonly/>
           </div>
           <div class="container">
             <label class="label">Téléphone</label>
-            <input class="input data_client" type="text" id="client_phone" name="client_phone" />
+            <input class="input data_rdvconfirm readonly" type="text" id="rdvconfirm_clientphone" data-id="" data-table="rendezvous" data-column="clientphone" readonly/>
           </div>
           <div class="container">
             <label class="label">E-Mail</label>
-            <input class="input data_client" type="text" id="client_email" name="client_email" />
+            <input class="input data_rdvconfirm readonly" type="text" id="rdvconfirm_clientemail" data-id=""  data-table="rendezvous" data-column="clientemail" readonly/>
+          </div>
+          <div class="container">
+            <label class="label">Message</label>
+            <textarea class="input data_rdvconfirm readonly" style="width: 100%; height: 100px;" id="rdvconfirm_clientmessage" data-id=""  data-table="rendezvous" data-column="clientmessage" readonly></textarea>
           </div>
-          <fieldset>
-            <legend>Détails Rendez-Vous</legend>
-            <div class="row">
-              <div class="container">
-                <label class="label">Produit / Service</label>
-                <input class="input data_client readonly" type="text" id="client_product" name="client_product" readonly/>
-              </div>
-              <div class="container">
-                <label class="label">Produit / Service</label>
-                <input class="input data_client readonly" type="text" id="client_date" name="client_date" readonly/>
-              </div>
-              <div class="container">
-                <label class="label">Produit / Service</label>
-                <input class="input data_client readonly" type="text" id="client_hour" name="client_hour" readonly/>
-              </div>
-            </div>
-          </fieldset>
         </div>
         </form>
+      </fieldset>
+      <button class="button block red-white" id="btnrdvconfirm" onclick="rendezvous.confirm();">Envoyer la demande de Rendez-Vous</button>
+      <button class="button block red-white" id="btnrdvcancel" onclick="rendezvous.cancel();" style="display: none;">Annuler la demande de Rendez-Vous</button>
+    </div>
+  </div>
+  <div class="panel" id="pnl_rdvsended"  style="display:none;">
+    <div class="bar red-white">
+      <a class="bar-item button" href="index.html">
+        <span class="icon-home" style="font-size: 30px;"></span><br/>Home
+      </a>
+      <div class="bar-item">
+        <span class="large text-white">Rendez-Vous</span></span>
       </div>
-      <div class="panel" id="pnl_confirm">
-        
-      </div>-->
     </div>
-    <div class="bottom">
-      <div class="bar red-gold">
-        <button class="bar-item button red-gold" onclick="rendezvous.viewpanel('');"><span class="icon-circle-left" style="font-size: 30px;"></span><br/><span id="btnrdvprev">prev</span></button>
-        <button class="bar-item button red-gold right" onclick="rendezvous.viewpanel('');"><span class="icon-circle-right" style="font-size: 30px;"></span><br/><span id="btnrdvnext">next</span></button>
+    <div class="container">
+      <div class="card" id="statusmsg"><p>Merci,
+        <br>la demande à été envoyé,
+        <br>nous vous avons envoyé une confirmation à votre addresse email,
+        <br>En ou vous voulez annuler le rendez-vous, clickez sur le lien dans l'email.</p>
       </div>
     </div>
+  </div>
+</div>
+<script src="js/rendezvous.js" type="text/javascript"></script>
+