/**
@license
Copyright (c) 2016 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
*/
import '../polymer/polymer_bundled.min.js';
import {dom} from '../polymer/polymer_bundled.min.js';
/**
* `Polymer.IronScrollTargetBehavior` allows an element to respond to scroll
* events from a designated scroll target.
*
* Elements that consume this behavior can override the `_scrollHandler`
* method to add logic on the scroll event.
*
* @demo demo/scrolling-region.html Scrolling Region
* @demo demo/document.html Document Element
* @polymerBehavior
*/
export const IronScrollTargetBehavior = {
properties: {
/**
* Specifies the element that will handle the scroll event
* on the behalf of the current element. This is typically a reference to an
*element, but there are a few more posibilities:
*
* ### Elements id
*
*```html
* <div id="scrollable-element" style="overflow: auto;">
* <x-element scroll-target="scrollable-element">
* <!-- Content-->
* </x-element>
* </div>
*```
* In this case, the `scrollTarget` will point to the outer div element.
*
* ### Document scrolling
*
* For document scrolling, you can use the reserved word `document`:
*
*```html
* <x-element scroll-target="document">
* <!-- Content -->
* </x-element>
*```
*
* ### Elements reference
*
*```js
* appHeader.scrollTarget = document.querySelector('#scrollable-element');
*```
*
* @type {HTMLElement}
* @default document
*/
scrollTarget: {
type: HTMLElement,
value: function() {
return this._defaultScrollTarget;
}
}
},
observers: ['_scrollTargetChanged(scrollTarget, isAttached)'],
/**
* True if the event listener should be installed.
*/
_shouldHaveListener: true,
_scrollTargetChanged: function(scrollTarget, isAttached) {
var eventTarget;
if (this._oldScrollTarget) {
this._toggleScrollListener(false, this._oldScrollTarget);
this._oldScrollTarget = null;
}
if (!isAttached) {
return;
}
// Support element id references
if (scrollTarget === 'document') {
this.scrollTarget = this._doc;
} else if (typeof scrollTarget === 'string') {
var domHost = this.domHost;
this.scrollTarget = domHost && domHost.$ ?
domHost.$[scrollTarget] :
dom(this.ownerDocument).querySelector('#' + scrollTarget);
} else if (this._isValidScrollTarget()) {
this._oldScrollTarget = scrollTarget;
this._toggleScrollListener(this._shouldHaveListener, scrollTarget);
}
},
/**
* Runs on every scroll event. Consumer of this behavior may override this
* method.
*
* @protected
*/
_scrollHandler: function scrollHandler() {},
/**
* The default scroll target. Consumers of this behavior may want to customize
* the default scroll target.
*
* @type {Element}
*/
get _defaultScrollTarget() {
return this._doc;
},
/**
* Shortcut for the document element
*
* @type {Element}
*/
get _doc() {
return this.ownerDocument.documentElement;
},
/**
* Gets the number of pixels that the content of an element is scrolled
* upward.
*
* @type {number}
*/
get _scrollTop() {
if (this._isValidScrollTarget()) {
return this.scrollTarget === this._doc ? window.pageYOffset :
this.scrollTarget.scrollTop;
}
return 0;
},
/**
* Gets the number of pixels that the content of an element is scrolled to the
* left.
*
* @type {number}
*/
get _scrollLeft() {
if (this._isValidScrollTarget()) {
return this.scrollTarget === this._doc ? window.pageXOffset :
this.scrollTarget.scrollLeft;
}
return 0;
},
/**
* Sets the number of pixels that the content of an element is scrolled
* upward.
*
* @type {number}
*/
set _scrollTop(top) {
if (this.scrollTarget === this._doc) {
window.scrollTo(window.pageXOffset, top);
} else if (this._isValidScrollTarget()) {
this.scrollTarget.scrollTop = top;
}
},
/**
* Sets the number of pixels that the content of an element is scrolled to the
* left.
*
* @type {number}
*/
set _scrollLeft(left) {
if (this.scrollTarget === this._doc) {
window.scrollTo(left, window.pageYOffset);
} else if (this._isValidScrollTarget()) {
this.scrollTarget.scrollLeft = left;
}
},
/**
* Scrolls the content to a particular place.
*
* @method scroll
* @param {number|!{left: number, top: number}} leftOrOptions The left position or scroll options
* @param {number=} top The top position
* @return {void}
*/
scroll: function(leftOrOptions, top) {
var left;
if (typeof leftOrOptions === 'object') {
left = leftOrOptions.left;
top = leftOrOptions.top;
} else {
left = leftOrOptions;
}
left = left || 0;
top = top || 0;
if (this.scrollTarget === this._doc) {
window.scrollTo(left, top);
} else if (this._isValidScrollTarget()) {
this.scrollTarget.scrollLeft = left;
this.scrollTarget.scrollTop = top;
}
},
/**
* Gets the width of the scroll target.
*
* @type {number}
*/
get _scrollTargetWidth() {
if (this._isValidScrollTarget()) {
return this.scrollTarget === this._doc ? window.innerWidth :
this.scrollTarget.offsetWidth;
}
return 0;
},
/**
* Gets the height of the scroll target.
*
* @type {number}
*/
get _scrollTargetHeight() {
if (this._isValidScrollTarget()) {
return this.scrollTarget === this._doc ? window.innerHeight :
this.scrollTarget.offsetHeight;
}
return 0;
},
/**
* Returns true if the scroll target is a valid HTMLElement.
*
* @return {boolean}
*/
_isValidScrollTarget: function() {
return this.scrollTarget instanceof HTMLElement;
},
_toggleScrollListener: function(yes, scrollTarget) {
var eventTarget = scrollTarget === this._doc ? window : scrollTarget;
if (yes) {
if (!this._boundScrollHandler) {
this._boundScrollHandler = this._scrollHandler.bind(this);
eventTarget.addEventListener('scroll', this._boundScrollHandler);
}
} else {
if (this._boundScrollHandler) {
eventTarget.removeEventListener('scroll', this._boundScrollHandler);
this._boundScrollHandler = null;
}
}
},
/**
* Enables or disables the scroll event listener.
*
* @param {boolean} yes True to add the event, False to remove it.
*/
toggleScrollListener: function(yes) {
this._shouldHaveListener = yes;
this._toggleScrollListener(yes, this.scrollTarget);
}
};