123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, NgZone, Optional, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
- import { App } from '../app/app';
- import { Config } from '../../config/config';
- import { DomController } from '../../platform/dom-controller';
- import { Ion } from '../ion';
- import { isTabs } from '../../navigation/nav-util';
- import { isTrueProperty, removeArrayItem } from '../../util/util';
- import { Keyboard } from '../../platform/keyboard';
- import { NavController } from '../../navigation/nav-controller';
- import { Platform } from '../../platform/platform';
- import { ScrollView } from '../../util/scroll-view';
- import { ViewController } from '../../navigation/view-controller';
- export class EventEmitterProxy extends EventEmitter {
- subscribe(generatorOrNext, error, complete) {
- this.onSubscribe();
- return super.subscribe(generatorOrNext, error, complete);
- }
- }
- /**
- * @name Content
- * @description
- * The Content component provides an easy to use content area with
- * some useful methods to control the scrollable area. There should
- * only be one content in a single view component. If additional scrollable
- * elements are needed, use [ionScroll](../../scroll/Scroll).
- *
- *
- * The content area can also implement pull-to-refresh with the
- * [Refresher](../../refresher/Refresher) component.
- *
- * @usage
- * ```html
- * <ion-content>
- * Add your content here!
- * </ion-content>
- * ```
- *
- * To get a reference to the content component from a Page's logic,
- * you can use Angular's `@ViewChild` annotation:
- *
- * ```ts
- * import { Component, ViewChild } from '@angular/core';
- * import { Content } from 'ionic-angular';
- *
- * @Component({...})
- * export class MyPage{
- * @ViewChild(Content) content: Content;
- *
- * scrollToTop() {
- * this.content.scrollToTop();
- * }
- * }
- * ```
- *
- * @advanced
- *
- * ### Scroll Events
- *
- * Scroll events happen outside of Angular's Zones. This is for performance reasons. So
- * if you're trying to bind a value to any scroll event, it will need to be wrapped in
- * a `zone.run()`
- *
- * ```ts
- * import { Component, NgZone } from '@angular/core';
- * @Component({
- * template: `
- * <ion-header>
- * <ion-navbar>
- * <ion-title>{{scrollAmount}}</ion-title>
- * </ion-navbar>
- * </ion-header>
- * <ion-content (ionScroll)="scrollHandler($event)">
- * <p> Some realllllllly long content </p>
- * </ion-content>
- * `})
- * class E2EPage {
- * public scrollAmount = 0;
- * constructor( public zone: NgZone){}
- * scrollHandler(event) {
- * console.log(`ScrollEvent: ${event}`)
- * this.zone.run(()=>{
- * // since scrollAmount is data-binded,
- * // the update needs to happen in zone
- * this.scrollAmount++
- * })
- * }
- * }
- * ```
- *
- * This goes for any scroll event, not just `ionScroll`.
- *
- * ### Resizing the content
- *
- * If the height of `ion-header`, `ion-footer` or `ion-tabbar`
- * changes dynamically, `content.resize()` has to be called in order to update the
- * layout of `Content`.
- *
- *
- * ```ts
- * @Component({
- * template: `
- * <ion-header>
- * <ion-navbar>
- * <ion-title>Main Navbar</ion-title>
- * </ion-navbar>
- * <ion-toolbar *ngIf="showToolbar">
- * <ion-title>Dynamic Toolbar</ion-title>
- * </ion-toolbar>
- * </ion-header>
- * <ion-content>
- * <button ion-button (click)="toggleToolbar()">Toggle Toolbar</button>
- * </ion-content>
- * `})
- *
- * class E2EPage {
- * @ViewChild(Content) content: Content;
- * showToolbar: boolean = false;
- *
- * toggleToolbar() {
- * this.showToolbar = !this.showToolbar;
- * this.content.resize();
- * }
- * }
- * ```
- *
- *
- * Scroll to a specific position
- *
- * ```ts
- * import { Component, ViewChild } from '@angular/core';
- * import { Content } from 'ionic-angular';
- *
- * @Component({
- * template: `<ion-content>
- * <button ion-button (click)="scrollTo()">Down 500px</button>
- * </ion-content>`
- * )}
- * export class MyPage{
- * @ViewChild(Content) content: Content;
- *
- * scrollTo() {
- * // set the scrollLeft to 0px, and scrollTop to 500px
- * // the scroll duration should take 200ms
- * this.content.scrollTo(0, 500, 200);
- * }
- * }
- * ```
- *
- */
- export class Content extends Ion {
- constructor(config, _plt, _dom, elementRef, renderer, _app, _keyboard, _zone, viewCtrl, navCtrl) {
- super(config, elementRef, renderer, 'content');
- this._plt = _plt;
- this._dom = _dom;
- this._app = _app;
- this._keyboard = _keyboard;
- this._zone = _zone;
- /** @internal */
- this._scrollPadding = 0;
- /** @internal */
- this._inputPolling = false;
- /** @internal */
- this._hasRefresher = false;
- /** @internal */
- this._imgs = [];
- /** @internal */
- this._scrollDownOnLoad = false;
- /**
- * @output {ScrollEvent} Emitted when the scrolling first starts.
- */
- this.ionScrollStart = new EventEmitterProxy();
- /**
- * @output {ScrollEvent} Emitted on every scroll event.
- */
- this.ionScroll = new EventEmitterProxy();
- /**
- * @output {ScrollEvent} Emitted when scrolling ends.
- */
- this.ionScrollEnd = new EventEmitterProxy();
- const enableScrollListener = () => this._scroll.enableEvents();
- this.ionScroll.onSubscribe = enableScrollListener;
- this.ionScrollStart.onSubscribe = enableScrollListener;
- this.ionScrollEnd.onSubscribe = enableScrollListener;
- this.statusbarPadding = config.getBoolean('statusbarPadding', false);
- this._imgReqBfr = config.getNumber('imgRequestBuffer', 1400);
- this._imgRndBfr = config.getNumber('imgRenderBuffer', 400);
- this._imgVelMax = config.getNumber('imgVelocityMax', 3);
- this._scroll = new ScrollView(_app, _plt, _dom);
- while (navCtrl) {
- if (isTabs(navCtrl)) {
- this._tabs = navCtrl;
- break;
- }
- navCtrl = navCtrl.parent;
- }
- if (viewCtrl) {
- this._viewCtrl = viewCtrl;
- // content has a view controller
- viewCtrl._setIONContent(this);
- viewCtrl._setIONContentRef(elementRef);
- this._viewCtrlReadSub = viewCtrl.readReady.subscribe(() => {
- this._viewCtrlReadSub.unsubscribe();
- this._readDimensions();
- });
- this._viewCtrlWriteSub = viewCtrl.writeReady.subscribe(() => {
- this._viewCtrlWriteSub.unsubscribe();
- this._writeDimensions();
- });
- }
- else {
- // content does not have a view controller
- _dom.read(this._readDimensions.bind(this));
- _dom.write(this._writeDimensions.bind(this));
- }
- }
- /**
- * Content height of the viewable area. This does not include content
- * which is outside the overflow area, or content area which is under
- * headers and footers. Read-only.
- *
- * @return {number}
- */
- get contentHeight() {
- return this._scroll.ev.contentHeight;
- }
- /**
- * Content width including content which is not visible on the screen
- * due to overflow. Read-only.
- *
- * @return {number}
- */
- get contentWidth() {
- return this._scroll.ev.contentWidth;
- }
- /**
- * Content height including content which is not visible on the screen
- * due to overflow. Read-only.
- *
- * @return {number}
- */
- get scrollHeight() {
- return this._scroll.ev.scrollHeight;
- }
- /**
- * Content width including content which is not visible due to
- * overflow. Read-only.
- *
- * @return {number}
- */
- get scrollWidth() {
- return this._scroll.ev.scrollWidth;
- }
- /**
- * The distance of the content's top to its topmost visible content.
- *
- * @return {number}
- */
- get scrollTop() {
- return this._scroll.ev.scrollTop;
- }
- /**
- * @param {number} top
- */
- set scrollTop(top) {
- this._scroll.setTop(top);
- }
- /**
- * The distance of the content's left to its leftmost visible content.
- *
- * @return {number}
- */
- get scrollLeft() {
- return this._scroll.ev.scrollLeft;
- }
- /**
- * @param {number} top
- */
- set scrollLeft(top) {
- this._scroll.setLeft(top);
- }
- /**
- * If the content is actively scrolling or not.
- *
- * @return {boolean}
- */
- get isScrolling() {
- return this._scroll.isScrolling;
- }
- /**
- * The current, or last known, vertical scroll direction. Possible
- * string values include `down` and `up`.
- *
- * @return {string}
- */
- get directionY() {
- return this._scroll.ev.directionY;
- }
- /**
- * The current, or last known, horizontal scroll direction. Possible
- * string values include `right` and `left`.
- *
- * @return {string}
- */
- get directionX() {
- return this._scroll.ev.directionX;
- }
- /**
- * @hidden
- */
- ngAfterViewInit() {
- (void 0) /* assert */;
- (void 0) /* assert */;
- const scroll = this._scroll;
- scroll.ev.fixedElement = this.getFixedElement();
- scroll.ev.scrollElement = this.getScrollElement();
- // subscribe to the scroll start
- scroll.onScrollStart = (ev) => {
- this.ionScrollStart.emit(ev);
- };
- // subscribe to every scroll move
- scroll.onScroll = (ev) => {
- // emit to all of our other friends things be scrolling
- this.ionScroll.emit(ev);
- this.imgsUpdate();
- };
- // subscribe to the scroll end
- scroll.onScrollEnd = (ev) => {
- this.ionScrollEnd.emit(ev);
- this.imgsUpdate();
- };
- }
- /**
- * @hidden
- */
- enableJsScroll() {
- this._scroll.enableJsScroll(this._cTop, this._cBottom);
- }
- /**
- * @hidden
- */
- ngOnDestroy() {
- this._scLsn && this._scLsn();
- this._viewCtrlReadSub && this._viewCtrlReadSub.unsubscribe();
- this._viewCtrlWriteSub && this._viewCtrlWriteSub.unsubscribe();
- this._viewCtrlReadSub = this._viewCtrlWriteSub = null;
- this._scroll && this._scroll.destroy();
- this._footerEle = this._scLsn = this._scroll = null;
- }
- /**
- * @hidden
- */
- getScrollElement() {
- return this._scrollContent.nativeElement;
- }
- /**
- * @private
- */
- getFixedElement() {
- return this._fixedContent.nativeElement;
- }
- /**
- * @hidden
- */
- onScrollElementTransitionEnd(callback) {
- this._plt.transitionEnd(this.getScrollElement(), callback);
- }
- /**
- * Scroll to the specified position.
- *
- * @param {number} x The x-value to scroll to.
- * @param {number} y The y-value to scroll to.
- * @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
- * @returns {Promise} Returns a promise which is resolved when the scroll has completed.
- */
- scrollTo(x, y, duration = 300, done) {
- (void 0) /* console.debug */;
- return this._scroll.scrollTo(x, y, duration, done);
- }
- /**
- * Scroll to the top of the content component.
- *
- * @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
- * @returns {Promise} Returns a promise which is resolved when the scroll has completed.
- */
- scrollToTop(duration = 300) {
- (void 0) /* console.debug */;
- return this._scroll.scrollToTop(duration);
- }
- /**
- * Scroll to the bottom of the content component.
- *
- * @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
- * @returns {Promise} Returns a promise which is resolved when the scroll has completed.
- */
- scrollToBottom(duration = 300) {
- (void 0) /* console.debug */;
- return this._scroll.scrollToBottom(duration);
- }
- /**
- * @input {boolean} If true, the content will scroll behind the headers
- * and footers. This effect can easily be seen by setting the toolbar
- * to transparent.
- */
- get fullscreen() {
- return this._fullscreen;
- }
- set fullscreen(val) {
- this._fullscreen = isTrueProperty(val);
- }
- /**
- * @input {boolean} If true, the content will scroll down on load.
- */
- get scrollDownOnLoad() {
- return this._scrollDownOnLoad;
- }
- set scrollDownOnLoad(val) {
- this._scrollDownOnLoad = isTrueProperty(val);
- }
- /**
- * @private
- */
- addImg(img) {
- this._imgs.push(img);
- }
- /**
- * @hidden
- */
- removeImg(img) {
- removeArrayItem(this._imgs, img);
- }
- /**
- * @hidden
- * DOM WRITE
- */
- setScrollElementStyle(prop, val) {
- const scrollEle = this.getScrollElement();
- if (scrollEle) {
- this._dom.write(() => {
- scrollEle.style[prop] = val;
- });
- }
- }
- /**
- * Returns the content and scroll elements' dimensions.
- * @returns {object} dimensions The content and scroll elements' dimensions
- * {number} dimensions.contentHeight content offsetHeight
- * {number} dimensions.contentTop content offsetTop
- * {number} dimensions.contentBottom content offsetTop+offsetHeight
- * {number} dimensions.contentWidth content offsetWidth
- * {number} dimensions.contentLeft content offsetLeft
- * {number} dimensions.contentRight content offsetLeft + offsetWidth
- * {number} dimensions.scrollHeight scroll scrollHeight
- * {number} dimensions.scrollTop scroll scrollTop
- * {number} dimensions.scrollBottom scroll scrollTop + scrollHeight
- * {number} dimensions.scrollWidth scroll scrollWidth
- * {number} dimensions.scrollLeft scroll scrollLeft
- * {number} dimensions.scrollRight scroll scrollLeft + scrollWidth
- */
- getContentDimensions() {
- const scrollEle = this.getScrollElement();
- const parentElement = scrollEle.parentElement;
- return {
- contentHeight: parentElement.offsetHeight - this._cTop - this._cBottom,
- contentTop: this._cTop,
- contentBottom: this._cBottom,
- contentWidth: parentElement.offsetWidth,
- contentLeft: parentElement.offsetLeft,
- scrollHeight: scrollEle.scrollHeight,
- scrollTop: scrollEle.scrollTop,
- scrollWidth: scrollEle.scrollWidth,
- scrollLeft: scrollEle.scrollLeft,
- };
- }
- /**
- * @hidden
- * DOM WRITE
- * Adds padding to the bottom of the scroll element when the keyboard is open
- * so content below the keyboard can be scrolled into view.
- */
- addScrollPadding(newPadding) {
- (void 0) /* assert */;
- if (newPadding === 0) {
- this._inputPolling = false;
- this._scrollPadding = -1;
- }
- if (newPadding > this._scrollPadding) {
- (void 0) /* console.debug */;
- this._scrollPadding = newPadding;
- var scrollEle = this.getScrollElement();
- if (scrollEle) {
- this._dom.write(() => {
- scrollEle.style.paddingBottom = (newPadding > 0) ? newPadding + 'px' : '';
- });
- }
- }
- }
- /**
- * @hidden
- * DOM WRITE
- */
- clearScrollPaddingFocusOut() {
- if (!this._inputPolling) {
- (void 0) /* console.debug */;
- this._inputPolling = true;
- this._keyboard.onClose(() => {
- (void 0) /* console.debug */;
- this.addScrollPadding(0);
- }, 200, 3000);
- }
- }
- /**
- * Tell the content to recalculate its dimensions. This should be called
- * after dynamically adding/removing headers, footers, or tabs.
- */
- resize() {
- this._dom.read(this._readDimensions.bind(this));
- this._dom.write(this._writeDimensions.bind(this));
- }
- /**
- * @hidden
- * DOM READ
- */
- _readDimensions() {
- const cachePaddingTop = this._pTop;
- const cachePaddingRight = this._pRight;
- const cachePaddingBottom = this._pBottom;
- const cachePaddingLeft = this._pLeft;
- const cacheHeaderHeight = this._hdrHeight;
- const cacheFooterHeight = this._ftrHeight;
- const cacheTabsPlacement = this._tabsPlacement;
- let tabsTop = 0;
- let scrollEvent;
- this._pTop = 0;
- this._pRight = 0;
- this._pBottom = 0;
- this._pLeft = 0;
- this._hdrHeight = 0;
- this._ftrHeight = 0;
- this._tabsPlacement = null;
- this._tTop = 0;
- this._fTop = 0;
- this._fBottom = 0;
- // In certain cases this._scroll is undefined
- // if that is the case then we should just return
- if (!this._scroll) {
- return;
- }
- scrollEvent = this._scroll.ev;
- let ele = this.getNativeElement();
- if (!ele) {
- (void 0) /* assert */;
- return;
- }
- let computedStyle;
- let tagName;
- let parentEle = ele.parentElement;
- let children = parentEle.children;
- for (var i = children.length - 1; i >= 0; i--) {
- ele = children[i];
- tagName = ele.tagName;
- if (tagName === 'ION-CONTENT') {
- scrollEvent.contentElement = ele;
- if (this._fullscreen) {
- // ******** DOM READ ****************
- computedStyle = getComputedStyle(ele);
- this._pTop = parsePxUnit(computedStyle.paddingTop);
- this._pBottom = parsePxUnit(computedStyle.paddingBottom);
- this._pRight = parsePxUnit(computedStyle.paddingRight);
- this._pLeft = parsePxUnit(computedStyle.paddingLeft);
- }
- }
- else if (tagName === 'ION-HEADER') {
- scrollEvent.headerElement = ele;
- // ******** DOM READ ****************
- this._hdrHeight = ele.clientHeight;
- }
- else if (tagName === 'ION-FOOTER') {
- scrollEvent.footerElement = ele;
- // ******** DOM READ ****************
- this._ftrHeight = ele.clientHeight;
- this._footerEle = ele;
- }
- }
- ele = parentEle;
- let tabbarEle;
- while (ele && ele.tagName !== 'ION-MODAL' && !ele.classList.contains('tab-subpage')) {
- if (ele.tagName === 'ION-TABS') {
- tabbarEle = ele.firstElementChild;
- // ******** DOM READ ****************
- this._tabbarHeight = tabbarEle.clientHeight;
- if (this._tabsPlacement === null) {
- // this is the first tabbar found, remember it's position
- this._tabsPlacement = ele.getAttribute('tabsplacement');
- }
- }
- ele = ele.parentElement;
- }
- // Tabs top
- if (this._tabs && this._tabsPlacement === 'top') {
- this._tTop = this._hdrHeight;
- tabsTop = this._tabs._top;
- }
- // Toolbar height
- this._cTop = this._hdrHeight;
- this._cBottom = this._ftrHeight;
- // Tabs height
- if (this._tabsPlacement === 'top') {
- this._cTop += this._tabbarHeight;
- }
- else if (this._tabsPlacement === 'bottom') {
- this._cBottom += this._tabbarHeight;
- }
- // Refresher uses a border which should be hidden unless pulled
- if (this._hasRefresher) {
- this._cTop -= 1;
- }
- // Fixed content shouldn't include content padding
- this._fTop = this._cTop;
- this._fBottom = this._cBottom;
- // Handle fullscreen viewport (padding vs margin)
- if (this._fullscreen) {
- this._cTop += this._pTop;
- this._cBottom += this._pBottom;
- }
- // ******** DOM READ ****************
- const contentDimensions = this.getContentDimensions();
- scrollEvent.scrollHeight = contentDimensions.scrollHeight;
- scrollEvent.scrollWidth = contentDimensions.scrollWidth;
- scrollEvent.contentHeight = contentDimensions.contentHeight;
- scrollEvent.contentWidth = contentDimensions.contentWidth;
- scrollEvent.contentTop = contentDimensions.contentTop;
- scrollEvent.contentBottom = contentDimensions.contentBottom;
- this._dirty = (cachePaddingTop !== this._pTop ||
- cachePaddingBottom !== this._pBottom ||
- cachePaddingLeft !== this._pLeft ||
- cachePaddingRight !== this._pRight ||
- cacheHeaderHeight !== this._hdrHeight ||
- cacheFooterHeight !== this._ftrHeight ||
- cacheTabsPlacement !== this._tabsPlacement ||
- tabsTop !== this._tTop ||
- this._cTop !== this.contentTop ||
- this._cBottom !== this.contentBottom);
- this._scroll.init(this.getScrollElement(), this._cTop, this._cBottom);
- // initial imgs refresh
- this.imgsUpdate();
- }
- /**
- * @hidden
- * DOM WRITE
- */
- _writeDimensions() {
- if (!this._dirty) {
- (void 0) /* console.debug */;
- return;
- }
- const scrollEle = this.getScrollElement();
- if (!scrollEle) {
- (void 0) /* assert */;
- return;
- }
- const fixedEle = this.getFixedElement();
- if (!fixedEle) {
- (void 0) /* assert */;
- return;
- }
- // Tabs height
- if (this._tabsPlacement === 'bottom' && this._cBottom > 0 && this._footerEle) {
- var footerPos = this._cBottom - this._ftrHeight;
- (void 0) /* assert */;
- // ******** DOM WRITE ****************
- this._footerEle.style.bottom = cssFormat(footerPos);
- }
- // Handle fullscreen viewport (padding vs margin)
- let topProperty = 'marginTop';
- let bottomProperty = 'marginBottom';
- let fixedTop = this._fTop;
- let fixedBottom = this._fBottom;
- if (this._fullscreen) {
- (void 0) /* assert */;
- (void 0) /* assert */;
- // adjust the content with padding, allowing content to scroll under headers/footers
- // however, on iOS you cannot control the margins of the scrollbar (last tested iOS9.2)
- // only add inline padding styles if the computed padding value, which would
- // have come from the app's css, is different than the new padding value
- topProperty = 'paddingTop';
- bottomProperty = 'paddingBottom';
- }
- // Only update top margin if value changed
- if (this._cTop !== this.contentTop) {
- (void 0) /* assert */;
- (void 0) /* assert */;
- // ******** DOM WRITE ****************
- scrollEle.style[topProperty] = cssFormat(this._cTop);
- // ******** DOM WRITE ****************
- fixedEle.style.marginTop = cssFormat(fixedTop);
- this.contentTop = this._cTop;
- }
- // Only update bottom margin if value changed
- if (this._cBottom !== this.contentBottom) {
- (void 0) /* assert */;
- (void 0) /* assert */;
- // ******** DOM WRITE ****************
- scrollEle.style[bottomProperty] = cssFormat(this._cBottom);
- // ******** DOM WRITE ****************
- fixedEle.style.marginBottom = cssFormat(fixedBottom);
- this.contentBottom = this._cBottom;
- }
- if (this._tabsPlacement !== null && this._tabs) {
- // set the position of the tabbar
- if (this._tabsPlacement === 'top') {
- // ******** DOM WRITE ****************
- this._tabs.setTabbarPosition(this._tTop, -1);
- }
- else {
- (void 0) /* assert */;
- // ******** DOM WRITE ****************
- this._tabs.setTabbarPosition(-1, 0);
- }
- }
- // Scroll the page all the way down after setting dimensions
- if (this._scrollDownOnLoad) {
- this.scrollToBottom(0);
- this._scrollDownOnLoad = false;
- }
- }
- /**
- * @hidden
- */
- imgsUpdate() {
- if (this._scroll.initialized && this._imgs.length && this.isImgsUpdatable()) {
- updateImgs(this._imgs, this.scrollTop, this.contentHeight, this.directionY, this._imgReqBfr, this._imgRndBfr);
- }
- }
- /**
- * @hidden
- */
- isImgsUpdatable() {
- // an image is only "updatable" if the content isn't scrolling too fast
- // if scroll speed is above the maximum velocity, then let current
- // requests finish, but do not start new requets or render anything
- // if scroll speed is below the maximum velocity, then it's ok
- // to start new requests and render images
- return Math.abs(this._scroll.ev.velocityY) < this._imgVelMax;
- }
- }
- Content.decorators = [
- { type: Component, args: [{
- selector: 'ion-content',
- template: '<div class="fixed-content" #fixedContent>' +
- '<ng-content select="[ion-fixed],ion-fab"></ng-content>' +
- '</div>' +
- '<div class="scroll-content" #scrollContent>' +
- '<ng-content></ng-content>' +
- '</div>' +
- '<ng-content select="ion-refresher"></ng-content>',
- host: {
- '[class.statusbar-padding]': 'statusbarPadding',
- '[class.has-refresher]': '_hasRefresher'
- },
- changeDetection: ChangeDetectionStrategy.OnPush,
- encapsulation: ViewEncapsulation.None
- },] },
- ];
- /** @nocollapse */
- Content.ctorParameters = () => [
- { type: Config, },
- { type: Platform, },
- { type: DomController, },
- { type: ElementRef, },
- { type: Renderer, },
- { type: App, },
- { type: Keyboard, },
- { type: NgZone, },
- { type: ViewController, decorators: [{ type: Optional },] },
- { type: NavController, decorators: [{ type: Optional },] },
- ];
- Content.propDecorators = {
- '_fixedContent': [{ type: ViewChild, args: ['fixedContent', { read: ElementRef },] },],
- '_scrollContent': [{ type: ViewChild, args: ['scrollContent', { read: ElementRef },] },],
- 'ionScrollStart': [{ type: Output },],
- 'ionScroll': [{ type: Output },],
- 'ionScrollEnd': [{ type: Output },],
- 'fullscreen': [{ type: Input },],
- 'scrollDownOnLoad': [{ type: Input },],
- };
- export function updateImgs(imgs, viewableTop, contentHeight, scrollDirectionY, requestableBuffer, renderableBuffer) {
- // ok, so it's time to see which images, if any, should be requested and rendered
- // ultimately, if we're scrolling fast then don't bother requesting or rendering
- // when scrolling is done, then it needs to do a check to see which images are
- // important to request and render, and which image requests should be aborted.
- // Additionally, images which are not near the viewable area should not be
- // rendered at all in order to save browser resources.
- const viewableBottom = (viewableTop + contentHeight);
- const priority1 = [];
- const priority2 = [];
- let img;
- // all images should be paused
- for (var i = 0, ilen = imgs.length; i < ilen; i++) {
- img = imgs[i];
- if (scrollDirectionY === 'up') {
- // scrolling up
- if (img.top < viewableBottom && img.bottom > viewableTop - renderableBuffer) {
- // scrolling up, img is within viewable area
- // or about to be viewable area
- img.canRequest = img.canRender = true;
- priority1.push(img);
- continue;
- }
- if (img.bottom <= viewableTop && img.bottom > viewableTop - requestableBuffer) {
- // scrolling up, img is within requestable area
- img.canRequest = true;
- img.canRender = false;
- priority2.push(img);
- continue;
- }
- if (img.top >= viewableBottom && img.top < viewableBottom + renderableBuffer) {
- // scrolling up, img below viewable area
- // but it's still within renderable area
- // don't allow a reset
- img.canRequest = img.canRender = false;
- continue;
- }
- }
- else {
- // scrolling down
- if (img.bottom > viewableTop && img.top < viewableBottom + renderableBuffer) {
- // scrolling down, img is within viewable area
- // or about to be viewable area
- img.canRequest = img.canRender = true;
- priority1.push(img);
- continue;
- }
- if (img.top >= viewableBottom && img.top < viewableBottom + requestableBuffer) {
- // scrolling down, img is within requestable area
- img.canRequest = true;
- img.canRender = false;
- priority2.push(img);
- continue;
- }
- if (img.bottom <= viewableTop && img.bottom > viewableTop - renderableBuffer) {
- // scrolling down, img above viewable area
- // but it's still within renderable area
- // don't allow a reset
- img.canRequest = img.canRender = false;
- continue;
- }
- }
- img.canRequest = img.canRender = false;
- img.reset();
- }
- // update all imgs which are viewable
- priority1.sort(sortTopToBottom).forEach(i => i.update());
- if (scrollDirectionY === 'up') {
- // scrolling up
- priority2.sort(sortTopToBottom).reverse().forEach(i => i.update());
- }
- else {
- // scrolling down
- priority2.sort(sortTopToBottom).forEach(i => i.update());
- }
- }
- function sortTopToBottom(a, b) {
- if (a.top < b.top) {
- return -1;
- }
- if (a.top > b.top) {
- return 1;
- }
- return 0;
- }
- function parsePxUnit(val) {
- return (val.indexOf('px') > 0) ? parseInt(val, 10) : 0;
- }
- function cssFormat(val) {
- return (val > 0 ? val + 'px' : '');
- }
- //# sourceMappingURL=content.js.map
|