Source: Presentation.js

(function (_super) {

  var ns = 'hui_presentation';

  /**
   * Vertical rows
   * @class
   * @augments hui.ui.Component
   * @param {Object} options
   */
  hui.ui.Presentation = function(options) {
    _super.call(this, options);
    this.items = [];
    this.images = [];
    hui.cls.add(this.element,'hui-is-light');
    this.nativeScroll = !!navigator.userAgent.match('iPhone|iPad|iPod|Safari') && !window.chrome && hui.browser.webkitVersion > 603;
    //this.nativeScroll = false;
    this._attach();
  };

  hui.ui.Presentation.create = function(options) {
    options = options || {};
    var makeIcon = function(body, size) {
      return '<svg class="' + ns + '_icon" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + size + ' ' + size + '">' + body + '</svg>';
    };
    var close = makeIcon(
      '<line class="' + ns + '_line" x1="1" y1="1" x2="31" y2="31"/>' +
      '<line class="' + ns + '_line" x1="1" y1="31" x2="31" y2="1"/>'
    );
    close = makeIcon('<path class="' + ns + '_line" d="M1,1l30,30 M1,31L31,1"/>', 32);
    close = makeIcon('<path class="' + ns + '_line" d="M1,1l20,20 M1,21L21,1"/>', 22);
    var right = makeIcon('<path class="' + ns + '_line" d="M8.5,31l15-15L8.5,1"/>', 32);
    var left = makeIcon('<path class="' + ns + '_line" d="M23.5,1l-15,15l15,15"/>', 32);
    options.element = hui.build('div', {
      'class' : ns,
      html : '<div class="' + ns + '_viewer id-viewer"><div class="' + ns + '_items id-items"></div></div>' +
        '<div class="' + ns + '_thumbnails id-thumbs"></div>'+
        '<div class="' + ns + '_close id-close">' + close + '</div>'+
        '<div class="' + ns + '_arrow ' + ns + '_next id-next">' + right + '</div>'+
        '<div class="' + ns + '_arrow ' + ns + '_previous id-previous">' + left + '</div>',
      parent: document.body
    });
    if (!hui.browser.touch) {
      hui.cls.add(options.element,'hui-is-mouse');
    }
    return new hui.ui.Presentation(options);
  };

  hui.ui.Presentation.prototype = {
    position: 0,
    index : 0,
    width: 0,
    animating: false,
    nodes : {
      viewer : '.id-viewer',
      items : '.id-items',
      thumbs : '.id-thumbs',
      close : '.id-close',
      next : '.id-next',
      previous : '.id-previous'
    },
    _attach : function() {
      hui.on(this.nodes.close,'tap',this.close, this);
      hui.on(this.nodes.next,'tap',this.next, this);
      hui.on(this.nodes.previous,'tap',this.previous, this);
      hui.on(this.nodes.thumbs,'tap',this._tapThumbs, this);
      if (this.nativeScroll) {
        hui.cls.add(this.element,'hui-is-native-scroll');
        this._attachNative();
      } else {
        this._attachDrag();
      }
    },
    close : function(e) {
      if (e) hui.event(e).stop();
      hui.cls.remove(this.element,'hui-is-open');
      this._lockScroll(false);
    },
    _attachNative : function() {
      var x,y;
      var moved = false;
      hui.listen(this.element,'touchstart',function(e) {
        moved = false;
      }.bind(this));
      hui.listen(this.element,'touchmove',function(e) {
        if (e.touches && e.touches.length == 2) {
          if (x===undefined) {x = e.pageX;}
          if (y===undefined) {y = e.pageY;}
          var img = this.images[this.index];
          var newX = e.pageX - x + img.x;
          var newY = e.pageY - y + img.y;
          var scale = (e.scale) * (img.scale);
          img.newScale = scale;
          img.newX = newX;
          img.newY = newY;
          img.node.style.transform =  'scale(' + scale + ') translate(' + (newX)/scale + 'px,' + (newY)/scale + 'px)';
        }
        if (e.touches && e.touches.length > 1) {
          e.preventDefault();
        }
        moved = true;
        //console.log('Scale', e.scale);
      }.bind(this));
      hui.listen(this.element,'touchend',function(e) {
        x = y = undefined;
        this.images.forEach(function(img) {
          if (img.newScale) {
            img.scale = img.newScale;
            img.newScale = undefined;
          }
          if (img.newX) {
            img.x = img.newX;
            img.newX = undefined;
          }
          if (img.newY) {
            img.y = img.newY;
            img.newY = undefined;
          }
        });
        if (!moved && hui.dom.isDescendantOrSelf(e.target, this.nodes.viewer)) {
          var img = this.images[this.index];
          if (img.scale != 1) {
            img.node.style.transition = 'transform .3s';
            img.node.style.transform = 'scale(1) translate(0px,0px)';
            setTimeout(function() {
              img.node.style.transition = '';
            },350);
            img.scale = 1;
            img.x = img.y = 0;
          } else {
            this._toggleThumbs();
          }
        }
      }.bind(this));
      var timer;
      var scrollEnd = function() {
        var width = this.nodes.viewer.clientWidth;
        var scrl = this.nodes.viewer.scrollLeft;
        this.index = Math.round(scrl / width);
      }.bind(this);
      hui.listen(this.nodes.viewer,'scroll',function(e) {
        //console.log('Scroll', e);
        clearTimeout(timer);
        timer = setTimeout(scrollEnd,150);
      });
    },
    _originalScroll : 0,
    _lockScroll : function(lock) {
      if (lock) {
        this._originalScroll = document.body.scrollTop;
      }
      lock = lock ? 'hidden' : '';
      document.body.parentNode.style.overflow = lock;
      document.body.style.overflow = lock;
      if (!lock) {
        document.body.scrollTop = this._originalScroll;
      }
    },
    /** Go to the previous image
     * @param {Boolean} user If it is initiated by the user
     */
    previous : function(e) {
      if (e) hui.stop(e);
      this.index--;
      var num = 1;
      if (this.index < 0) {
        this.index = this.items.length - 1;
        num = this.items.length;
      }
      this._goToImage(true,num,true);
    },
    /** Go to the next image
     * @param {Boolean} user If it is initiated by the user
     */
    next : function(e) {
      if (e) hui.stop(e);
      this.index++;
      var num = 1;
      if (this.index==this.items.length) {
        this.index = 0;
        num = this.items.length;
      }
      this._goToImage(true,num,true);
    },
    show : function(params) {
      params = params || {};
      if (params.source) {
        var pos = hui.position.get(params.source);
        hui.cls.add(this.element,'hui-is-minimized');
      }
      hui.cls.add(this.element,'hui-is-open');
      this._calculateSize();
      if (params.items) {
        this.items = params.items;
        this._rebuild();
      }
      if (hui.isNumber(params.index)) {
        this.index = params.index;
        this._goToImage(false, 0, false);
      }
      window.setTimeout(function() {
        this._lockScroll(true);        
      }.bind(this),100)
    },
    _tapThumbs : function(e) {
      e = hui.event(e);
      var thumb = e.findByClass(ns + '_thumbnail');
      if (thumb) {
        var index = parseInt(thumb.getAttribute('data'),10);
        this._goTo(index);
      }
    },
    _rebuild : function() {
      this.images = [];
      this.nodes.items.innerHTML = '';
      this.nodes.thumbs.innerHTML = '';
      for (var i = 0; i < this.items.length; i++) {
        var url = hui.ui.callDelegates(this,'getPreview',{item: this.items[i], index: i});
        var item = hui.build('div',{parent : this.nodes.items, 'class' : ns+'_item'});
        var image = hui.build('div',{parent : item, 'class' : ns+'_image'});
        var content = hui.build('div',{parent : image, 'class' : ns+'_image_content'});
        var thumb = hui.build('div',{parent : this.nodes.thumbs, 'class' : ns+'_thumbnail', data:i});
        if (url) {
          content.style.backgroundImage = 'url(' + url + ')';
          thumb.style.backgroundImage = 'url(' + url + ')';
        }
        this.images.push({node:content,scale:1,x:0,y:0});
      }
      hui.cls.set(this.element, 'hui-is-multiple', this.items.length > 1);
      this._updateImages();
    },
    _updateImages : function() {
      if (!this.items.length) { return; }
      var width = this.nodes.viewer.clientWidth,
        height = this.nodes.viewer.clientHeight,
      thumbs = hui.get.children(this.nodes.thumbs);
      var thumbSize = thumbs[0].clientWidth;
      var load = function(url,node) {
        var img = new Image();
        img.onload = function() {
          node.style.backgroundImage = "url('" + url + "')";
        };
        img.src = url;
      };
      var ratio = window.devicePixelRatio > 1 ? 2 : 1;
      for (var i = 0; i < this.items.length; i++) {
        var item = this.items[i];
        var url = hui.ui.callDelegates(this,'getImage',{item: item, width: width * ratio, height: height * ratio});
        load(url, this.images[i].node);
        //images[i].style.backgroundImage = "url('" + url + "')";
        var thmbUrl = hui.ui.callDelegates(this,'getImage',{item: item, width: thumbSize * ratio, height: thumbSize * ratio});
        load(thmbUrl, thumbs[i]);
        //thumbs[i].style.backgroundImage = "url('" + url + "')";
      }
    },
    _calculateSize : function() {
      this.width = this.nodes.viewer.clientWidth;
    },
    $$layout : function() {
      this._calculateSize();
      this._updateImages();
    },
    _draw : function() {
      this.nodes.items.style.transform = 'translate3d(' + this.position + 'px,0,0)';
    },
    _attachDrag : function() {
      var initial = 0;
      var left = 0;
      var scrl = 0;
      var viewer = this.nodes.viewer;
      var inner = this.nodes.items;
      var max = 0;
      var stmp;
      var currentSlide;
      var speed = 0;
      hui.drag.attach({
        touch : true,
        element : viewer,
        $check : function() {
          return !this.animating;
        }.bind(this),
        $startMove : function(e) {
          initial = e.getLeft();
          scrl = this.position;
          max = (this.items.length-1) * this.width * -1;
          stmp = e.event.timeStamp;
          speed = 0;
        }.bind(this),
        $move : function(e) {
          var latestLeft = left;
          left = e.getLeft();
          var pos = (scrl - (initial - left));
          if (pos > 0) {
            pos = (Math.exp(pos * -0.013) -1) * -80;
          }
          if (pos < max) {
            pos = (Math.exp((pos - max) * 0.013) -1) * 80 + max;
          }
          this.position = pos;
          var dur = (e.event.timeStamp - stmp);
          var curSpeed = dur > 0 ? (latestLeft - left) / dur : 0;
          speed = (speed + curSpeed) / 2;
          //console.log(speed);
          stmp = e.event.timeStamp;
          this._draw();
        }.bind(this),
        $endMove : function(e) {
          var func = (initial - left) < 0 ? Math.floor : Math.ceil;
          this.index = func(this.position * -1 / this.width);
          var num = this.items.length - 1;
          if (this.index==this.items.length) {
            this.index = 0;
            this.index = this.items.length - 1;
          } else if (this.index < 0) {
            this.index = this.items.length - 1;
            this.index = 0;
          } else {
            num = 1;
          }

          this._goToImage(true,num,false,true, Math.abs(speed));
        }.bind(this),
        $notMoved : this._toggleThumbs.bind(this)
      });
    },
    _toggleThumbs : function() {
      if (this.items.length > 1) {
        hui.cls.toggle(this.element,'hui-is-thumbnails');
      }
    },
    _goTo : function(index) {
      if (this.index == index) {
        return;
      }
      var diff = Math.abs(this.index - index);
      this.index = index;
      this._goToImage(true, diff, false);
    },
    _goToImage : function(animate,num,user,drag,speed) {
      if (this.nativeScroll) {
        var viewer = this.nodes.viewer,
          scrl = viewer.clientWidth * this.index;
        if (animate) {
          hui.animate({
            node: viewer,
            duration: 500,
            property: 'scrollLeft',
            value: scrl,
            ease: hui.ease.fastSlow
          });
        } else {
          if (hui.browser.webkitVersion < 603) {
            setTimeout(function() {
              viewer.scrollTo(scrl,0);
            });
          } else {
            this.nodes.viewer.scrollLeft = scrl;
          }
        }
        return;
      }
      speed = speed || 2;
      var initial = this.position;
      var target = this.position = this.index * (this.width) * -1;
      if (animate) {
        var duration, ease;
        if (drag) {
          duration = 300 / speed;
          ease = hui.ease.quadOut;
          duration = Math.min(800,Math.max(200, duration));
        }
        else if (num > 1) {
          duration = Math.min(num * 500, 2000);
          ease = hui.ease.fastSlow;
        } else {
          var end = this.index === 0 || this.index === this.items.length - 1;
          ease = (end ? hui.ease.elastic : hui.ease.fastSlow);
          if (!user) {
            ease = hui.ease.fastSlow;
          }
          duration = (end ? 1000 : 1000 / speed);
        }
        this.animating = true;
        hui.animate({
          node : this.nodes.items,
          //css : {marginLeft : target + 'px'},
          duration : duration,
          ease : ease,
          $render : function(node,v) {
            this.position = initial + (target - initial) * v;
            this._draw();
          }.bind(this),
          $complete : function() {
            this.animating = false;
          }.bind(this)
        });
      } else {
        this._draw();
      }
    }
  };

  hui.extend(hui.ui.Presentation, _super);

  hui.define('hui.ui.Presentation', hui.ui.Presentation);

})(hui.ui.Component);