<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->

<!--
`paper-tabs` is a `core-selector` styled to look like tabs. Tabs make it easy to 
explore and switch between different views or functional aspects of an app, or 
to browse categorized data sets.

Use `selected` property to get or set the selected tab.

Example:

    <paper-tabs selected="0">
      <paper-tab>TAB 1</paper-tab>
      <paper-tab>TAB 2</paper-tab>
      <paper-tab>TAB 3</paper-tab>
    </paper-tabs>

See <a href="#paper-tab">paper-tab</a> for more information about 
`paper-tab`.

A common usage for `paper-tabs` is to use it along with `core-pages` to switch
between different views.

    <paper-tabs selected="{{selected}}">
      <paper-tab>Tab 1</paper-tab>
      <paper-tab>Tab 2</paper-tab>
      <paper-tab>Tab 3</paper-tab>
    </paper-tabs>
  
    <core-pages selected="{{selected}}">
      <div>Page 1</div>
      <div>Page 2</div>
      <div>Page 3</div>
    </core-pages>
    
`paper-tabs` adapt to mobile/narrow layout when there is a `core-narrow` class set
on itself or any of its ancestors.

To use links in tabs, add `link` attribute to `paper-tabs` and put an `<a>`
element in `paper-tab`.

Example:

    <paper-tabs selected="0" link>
      <paper-tab>
        <a href="#link1" horizontal center-center layout>TAB ONE</a>
      </paper-tab>
      <paper-tab>
        <a href="#link2" horizontal center-center layout>TAB TWO</a>
      </paper-tab>
      <paper-tab>
        <a href="#link3" horizontal center-center layout>TAB THREE</a>
      </paper-tab>
    </paper-tabs>

Styling tabs:

To change the sliding bar color:

    paper-tabs.pink::shadow #selectionBar {
      background-color: #ff4081;
    }
    
To change the ink ripple color:

    paper-tabs.pink paper-tab::shadow #ink {
      color: #ff4081;
    }

@group Paper Elements
@element paper-tabs
@extends core-selector
@mixins Polymer.CoreResizable https://github.com/polymer/core-resizable
@homepage github.io
-->

<link rel="import" href="../core-selector/core-selector.html">
<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../core-resizable/core-resizable.html">
<link rel="import" href="paper-tab.html">

<polymer-element name="paper-tabs" extends="core-selector" attributes="noink nobar noslide scrollable hideScrollButton" role="tablist" horizontal center layout>
<template>

  <link rel="stylesheet" href="paper-tabs.css">
  
  <div class="scroll-button" hidden?="{{!scrollable || hideScrollButton}}">
    <paper-icon-button icon="chevron-left" class="{{ {hidden: leftHidden} | tokenList }}" on-down="{{holdLeft}}" on-up="{{releaseHold}}"></paper-icon-button>
  </div>
  
  <div id="tabsContainer" class="{{ {scrollable: scrollable} | tokenList }}" flex on-scroll="{{scroll}}" on-trackstart="{{trackStart}}">
  
    <div id="tabsContent" horizontal layout?="{{!scrollable}}">
      <shadow></shadow>
      <div id="selectionBar" hidden?="{{nobar}}" on-transitionend="{{barTransitionEnd}}"></div>
    </div>
    
  </div>
  
  <div class="scroll-button" hidden?="{{!scrollable || hideScrollButton}}">
    <paper-icon-button icon="chevron-right" class="{{ {hidden: rightHidden} | tokenList }}" on-down="{{holdRight}}" on-up="{{releaseHold}}"></paper-icon-button>
  </div>
    
</template>
<script>

  Polymer(Polymer.mixin({
    
    /**
     * If true, ink ripple effect is disabled.
     *
     * @attribute noink
     * @type boolean
     * @default false
     */
    noink: false,
    
    /**
     * If true, the bottom bar to indicate the selected tab will not be shown.
     *
     * @attribute nobar
     * @type boolean
     * @default false
     */
    nobar: false,
    
    /**
     * If true, the slide effect for the bottom bar is disabled.
     *
     * @attribute noslide
     * @type boolean
     * @default false
     */
    noslide: false,
    
    /**
     * If true, tabs are scrollable and the tab width is based on the label width.
     *
     * @attribute scrollable
     * @type boolean
     * @default false
     */
    scrollable: false,
    
    /**
     * If true, dragging on the tabs to scroll is disabled.
     *
     * @attribute disableDrag
     * @type boolean
     * @default false
     */
    disableDrag: false,
    
    /**
     * If true, scroll buttons (left/right arrow) will be hidden for scrollable tabs.
     *
     * @attribute hideScrollButton
     * @type boolean
     * @default false
     */
    hideScrollButton: false,

    eventDelegates: {
      'core-resize': 'resizeHandler'
    },
    
    activateEvent: 'tap',
    
    step: 10,
    
    holdDelay: 10,
    
    ready: function() {
      this.super();
      this._trackxHandler = this.trackx.bind(this);
      Polymer.addEventListener(this.$.tabsContainer, 'trackx', this._trackxHandler);
      this._tabsObserver = new MutationObserver(this.updateBar.bind(this));
    },
    
    domReady: function() {
      this.async('resizeHandler');
      this._tabsObserver.observe(this, {childList: true, subtree: true, characterData: true});
    },

    attached: function() {
      this.resizableAttachedHandler();
    },
    
    detached: function() {
      Polymer.removeEventListener(this.$.tabsContainer, 'trackx', this._trackxHandler);
      this._tabsObserver.disconnect();
      this.resizableDetachedHandler();
    },
    
    trackStart: function(e) {
      if (!this.scrollable || this.disableDrag) {
        return;
      }
      var t = e.target;
      if (t && t.cancelRipple) {
        t.cancelRipple();
      }
      this._startx = this.$.tabsContainer.scrollLeft;
      e.preventTap();
    },
    
    trackx: function(e) {
      if (!this.scrollable || this.disableDrag) {
        return;
      }
      this.$.tabsContainer.scrollLeft = this._startx - e.dx;
    },
    
    resizeHandler: function() {
      this.scroll();
      this.updateBar();
    },
    
    scroll: function() {
      if (!this.scrollable) {
        return;
      }
      var tc = this.$.tabsContainer;
      var l = tc.scrollLeft;
      this.leftHidden = l === 0;
      this.rightHidden = l === Math.max(0, (tc.scrollWidth - tc.clientWidth));
    },
    
    holdLeft: function() {
      this.holdJob = setInterval(this.scrollToLeft.bind(this), this.holdDelay);
    },
    
    holdRight: function() {
      this.holdJob = setInterval(this.scrollToRight.bind(this), this.holdDelay);
    },
    
    releaseHold: function() {
      clearInterval(this.holdJob);
      this.holdJob = null;
    },
    
    scrollToLeft: function() {
      this.$.tabsContainer.scrollLeft -= this.step;
    },
    
    scrollToRight: function() {
      this.$.tabsContainer.scrollLeft += this.step;
    },
    
    /**
     * Invoke this to update the size and position of the bottom bar.  Usually
     * you only need to call this if the `paper-tabs` is initially hidden and
     * later becomes visible.
     *
     * @method updateBar
     */
    updateBar: function() {
      this.async('selectedItemChanged');
    },
    
    selectedItemChanged: function(old) {
      var oldIndex = this.selectedIndex;
      this.super(arguments);
      var s = this.$.selectionBar.style;
      
      if (!this.selectedItem) {
        s.width = 0;
        s.left = 0;
        return;
      } 
      
      var r = this.$.tabsContent.getBoundingClientRect();
      this._w = r.width;
      this._l = r.left;
      
      r = this.selectedItem.getBoundingClientRect();
      this._sw = r.width;
      this._sl = r.left;
      this._sOffsetLeft = this._sl - this._l; 
      
      if (this.noslide || old == null) {
        this.positionBarForSelected();
        return;
      }
      
      var oldRect = old.getBoundingClientRect();
      
      var m = 5;
      this.$.selectionBar.classList.add('expand');
      if (oldIndex < this.selectedIndex) {
        s.width = this.calcPercent(this._sl + this._sw - oldRect.left) - m + '%';
        this._transitionCounter = 1;
      } else {
        s.width = this.calcPercent(oldRect.left + oldRect.width - this._sl) - m + '%';
        s.left = this.calcPercent(this._sOffsetLeft) + m + '%';
        this._transitionCounter = 2;
      }
      if (this.scrollable) {
        this.scrollToSelectedIfNeeded();
      }
    },
    
    scrollToSelectedIfNeeded: function() {
      var scrollLeft = this.$.tabsContainer.scrollLeft;
      // scroll to selected if needed
      if (this._sOffsetLeft + this._sw < scrollLeft || 
          this._sOffsetLeft - scrollLeft > this.$.tabsContainer.offsetWidth) {
        this.$.tabsContainer.scrollLeft = this._sOffsetLeft;
      }
    },
    
    positionBarForSelected: function() {
      var s = this.$.selectionBar.style;
      s.width = this.calcPercent(this._sw) + '%';
      s.left = this.calcPercent(this._sOffsetLeft) + '%';
    },
    
    calcPercent: function(w) {
      return 100 * w / this._w;
    },
    
    barTransitionEnd: function(e) {
      this._transitionCounter--;
      var cl = this.$.selectionBar.classList;
      if (cl.contains('expand') && !this._transitionCounter) {
        cl.remove('expand');
        cl.add('contract');
        this.positionBarForSelected();
      } else if (cl.contains('contract')) {
        cl.remove('contract');
      }
    }
    
  }, Polymer.CoreResizable));
  
</script>
</polymer-element>
