import _typeof from 'babel-runtime/helpers/typeof'; import _setImmediate from 'babel-runtime/core-js/set-immediate'; import _Promise from 'babel-runtime/core-js/promise'; import _WeakMap from 'babel-runtime/core-js/weak-map'; import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; import _createClass from 'babel-runtime/helpers/createClass'; import _inherits from 'babel-runtime/helpers/inherits'; /* Copyright 2013-2015 ASIAL CORPORATION Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import onsElements from '../../ons/elements'; import util from '../../ons/util'; import internal from '../../ons/internal'; import SwipeReveal from '../../ons/internal/swipe-reveal'; import AnimatorFactory from '../../ons/internal/animator-factory'; import NavigatorAnimator from './animator'; import IOSSlideNavigatorAnimator from './ios-slide-animator'; import IOSLiftNavigatorAnimator from './ios-lift-animator'; import IOSFadeNavigatorAnimator from './ios-fade-animator'; import MDSlideNavigatorAnimator from './md-slide-animator'; import MDLiftNavigatorAnimator from './md-lift-animator'; import MDFadeNavigatorAnimator from './md-fade-animator'; import NoneNavigatorAnimator from './none-animator'; import platform from '../../ons/platform'; import contentReady from '../../ons/content-ready'; import BaseElement from '../base/base-element'; import deviceBackButtonDispatcher from '../../ons/internal/device-back-button-dispatcher'; import { PageLoader, defaultPageLoader, instantPageLoader } from '../../ons/page-loader'; var _animatorDict = { 'default': function _default() { return platform.isAndroid() ? MDFadeNavigatorAnimator : IOSSlideNavigatorAnimator; }, 'slide': function slide() { return platform.isAndroid() ? MDSlideNavigatorAnimator : IOSSlideNavigatorAnimator; }, 'lift': function lift() { return platform.isAndroid() ? MDLiftNavigatorAnimator : IOSLiftNavigatorAnimator; }, 'fade': function fade() { return platform.isAndroid() ? MDFadeNavigatorAnimator : IOSFadeNavigatorAnimator; }, 'slide-ios': IOSSlideNavigatorAnimator, 'slide-md': MDSlideNavigatorAnimator, 'lift-ios': IOSLiftNavigatorAnimator, 'lift-md': MDLiftNavigatorAnimator, 'fade-ios': IOSFadeNavigatorAnimator, 'fade-md': MDFadeNavigatorAnimator, 'none': NoneNavigatorAnimator }; var rewritables = { /** * @param {Element} navigatorSideElement * @param {Function} callback */ ready: function ready(navigatorElement, callback) { callback(); } }; var verifyPageElement = function verifyPageElement(el) { return el.nodeName !== 'ONS-PAGE' && util.throw('Only page elements can be children of navigator'); }; /** * @element ons-navigator * @category navigation * @description * [en] * A component that provides page stack management and navigation. Stack navigation is the most common navigation pattern for mobile apps. * * When a page is pushed on top of the stack it is displayed with a transition animation. When the user returns to the previous page the top page will be popped from the top of the stack and hidden with an opposite transition animation. * [/en] * [ja][/ja] * @codepen yrhtv * @tutorial vanilla/Reference/navigator * @guide lifecycle.html#events * [en]Overview of page events[/en] * [ja]Overview of page events[/ja] * @seealso ons-toolbar * [en]The `` component is used to display a toolbar on the top of a page.[/en] * [ja][/ja] * @seealso ons-back-button * [en]The `` component lets the user return to the previous page.[/en] * [ja][/ja] * @example * * * *
* Title *
*
*

* * Push page * *

*
*
* * */ var NavigatorElement = function (_BaseElement) { _inherits(NavigatorElement, _BaseElement); _createClass(NavigatorElement, [{ key: 'animatorFactory', /** * @attribute page * @initonly * @type {String} * @description * [en]First page to show when navigator is initialized.[/en] * [ja]ナビゲーターが初期化された時に表示するページを指定します。[/ja] */ /** * @attribute swipeable * @type {Boolean} * @description * [en]Enable iOS "swipe to pop" feature.[/en] * [ja][/ja] */ /** * @attribute swipe-target-width * @type {String} * @default 20px * @description * [en]The width of swipeable area calculated from the edge (in pixels). Use this to enable swipe only when the finger touch on the screen edge.[/en] * [ja]スワイプの判定領域をピクセル単位で指定します。画面の端から指定した距離に達するとページが表示されます。[/ja] */ /** * @attribute swipe-threshold * @type {Number} * @default 0.2 * @description * [en]Specify how much the page needs to be swiped before popping. A value between `0` and `1`.[/en] * [ja][/ja] */ /** * @attribute animation * @type {String} * @default default * @description * [en] * Animation name. Available animations are `"slide"`, `"lift"`, `"fade"` and `"none"`. * * These are platform based animations. For fixed animations, add `"-ios"` or `"-md"` suffix to the animation name. E.g. `"lift-ios"`, `"lift-md"`. Defaults values are `"slide-ios"` and `"fade-md"` depending on the platform. * [/en] * [ja][/ja] */ /** * @attribute animation-options * @type {Expression} * @description * [en]Specify the animation's duration, timing and delay with an object literal. E.g. `{duration: 0.2, delay: 1, timing: 'ease-in'}`[/en] * [ja]アニメーション時のduration, timing, delayをオブジェクトリテラルで指定します。e.g. `{duration: 0.2, delay: 1, timing: 'ease-in'}`[/ja] */ /** * @event prepush * @description * [en]Fired just before a page is pushed.[/en] * [ja]pageがpushされる直前に発火されます。[/ja] * @param {Object} event [en]Event object.[/en] * @param {Object} event.navigator * [en]Component object.[/en] * [ja]コンポーネントのオブジェクト。[/ja] * @param {Object} event.currentPage * [en]Current page object.[/en] * [ja]現在のpageオブジェクト。[/ja] * @param {Function} event.cancel * [en]Call this function to cancel the push.[/en] * [ja]この関数を呼び出すと、push処理がキャンセルされます。[/ja] */ /** * @event prepop * @description * [en]Fired just before a page is popped.[/en] * [ja]pageがpopされる直前に発火されます。[/ja] * @param {Object} event [en]Event object.[/en] * @param {Object} event.navigator * [en]Component object.[/en] * [ja]コンポーネントのオブジェクト。[/ja] * @param {Object} event.currentPage * [en]Current page object.[/en] * [ja]現在のpageオブジェクト。[/ja] * @param {Function} event.cancel * [en]Call this function to cancel the pop.[/en] * [ja]この関数を呼び出すと、pageのpopがキャンセルされます。[/ja] */ /** * @event postpush * @description * [en]Fired just after a page is pushed.[/en] * [ja]pageがpushされてアニメーションが終了してから発火されます。[/ja] * @param {Object} event [en]Event object.[/en] * @param {Object} event.navigator * [en]Component object.[/en] * [ja]コンポーネントのオブジェクト。[/ja] * @param {Object} event.enterPage * [en]Object of the next page.[/en] * [ja]pushされたpageオブジェクト。[/ja] * @param {Object} event.leavePage * [en]Object of the previous page.[/en] * [ja]以前のpageオブジェクト。[/ja] */ /** * @event postpop * @description * [en]Fired just after a page is popped.[/en] * [ja]pageがpopされてアニメーションが終わった後に発火されます。[/ja] * @param {Object} event [en]Event object.[/en] * @param {Object} event.navigator * [en]Component object.[/en] * [ja]コンポーネントのオブジェクト。[/ja] * @param {Object} event.enterPage * [en]Object of the next page.[/en] * [ja]popされて表示されるページのオブジェクト。[/ja] * @param {Object} event.leavePage * [en]Object of the previous page.[/en] * [ja]popされて消えるページのオブジェクト。[/ja] */ get: function get() { return this._animatorFactory; } }]); function NavigatorElement() { _classCallCheck(this, NavigatorElement); var _this = _possibleConstructorReturn(this, (NavigatorElement.__proto__ || _Object$getPrototypeOf(NavigatorElement)).call(this)); _this._isRunning = false; _this._initialized = false; _this._pageLoader = defaultPageLoader; _this._pageMap = new _WeakMap(); _this._updateAnimatorFactory(); return _this; } /** * @property pageLoader * @type {PageLoader} * @description * [en]PageLoader instance. It can be overriden to change the way pages are loaded by this element. Useful for lib developers.[/en] * [ja]PageLoaderインスタンスを格納しています。[/ja] */ _createClass(NavigatorElement, [{ key: '_getPageTarget', value: function _getPageTarget() { return this._page || this.getAttribute('page'); } /** * @property page * @type {*} * @description * [en]Specify the page to be loaded during initialization. This value takes precedence over the `page` attribute. Useful for lib developers.[/en] * [ja]初期化時に読み込むページを指定します。`page`属性で指定した値よりも`page`プロパティに指定した値を優先します。[/ja] */ }, { key: 'connectedCallback', value: function connectedCallback() { var _this2 = this; this.onDeviceBackButton = this._onDeviceBackButton.bind(this); if (!platform.isAndroid() || this.getAttribute('swipeable') === 'force') { var swipeAnimator = void 0; this._swipe = new SwipeReveal({ element: this, getThreshold: function getThreshold() { return Math.max(0.2, parseFloat(_this2.getAttribute('swipe-threshold')) || 0); }, swipeMax: function swipeMax() { _this2._onSwipe && _this2._onSwipe(1, { duration: swipeAnimator.durationSwipe, timing: swipeAnimator.timingSwipe }); _this2[_this2.swipeMax ? 'swipeMax' : 'popPage']({ animator: swipeAnimator }); swipeAnimator = null; }, swipeMid: function swipeMid(distance, width) { _this2._onSwipe && _this2._onSwipe(distance / width); swipeAnimator.translate(distance, width, _this2.topPage.previousElementSibling, _this2.topPage); }, swipeMin: function swipeMin() { _this2._onSwipe && _this2._onSwipe(0, { duration: swipeAnimator.durationRestore, timing: swipeAnimator.timingSwipe }); swipeAnimator.restore(_this2.topPage.previousElementSibling, _this2.topPage); swipeAnimator = null; }, ignoreSwipe: function ignoreSwipe(event, distance) { // Basic conditions if (!_this2._isRunning && _this2.children.length > 1) { // Area or directional issues var area = parseInt(_this2.getAttribute('swipe-target-width') || 25, 10); if (event.gesture.direction === 'right' && area > distance) { // Swipes on ons-back-button and its children var isBB = function isBB(el) { return (/ons-back-button/i.test(el.tagName) ); }; if (!isBB(event.target) && !util.findParent(event.target, isBB, function (p) { return (/ons-page/i.test(p.tagName) ); })) { // Animator is swipeable var animation = (_this2.topPage.pushedOptions || {}).animation || _this2.animatorFactory._animation; var Animator = _animatorDict[animation] instanceof Function ? _animatorDict[animation].call() : _animatorDict[animation]; if (typeof Animator !== 'undefined' && Animator.swipeable) { swipeAnimator = new Animator(); // Prepare for the swipe return false; } } } } return true; // Ignore swipe } }); this.attributeChangedCallback('swipeable'); } if (this._initialized) { return; } this._initialized = true; var deferred = util.defer(); this.loaded = deferred.promise; rewritables.ready(this, function () { var show = !util.hasAnyComponentAsParent(_this2); var options = { animation: 'none', show: show }; if (_this2.pages.length === 0 && _this2._getPageTarget()) { _this2.pushPage(_this2._getPageTarget(), options).then(function () { return deferred.resolve(); }); } else if (_this2.pages.length > 0) { for (var i = 0; i < _this2.pages.length; i++) { verifyPageElement(_this2.pages[i]); } if (_this2.topPage) { contentReady(_this2.topPage, function () { return setTimeout(function () { deferred.resolve(); show && _this2.topPage._show(); _this2._updateLastPageBackButton(); }, 0); }); } } else { contentReady(_this2, function () { if (_this2.pages.length === 0 && _this2._getPageTarget()) { _this2.pushPage(_this2._getPageTarget(), options).then(function () { return deferred.resolve(); }); } else { deferred.resolve(); } }); } }); } }, { key: '_updateAnimatorFactory', value: function _updateAnimatorFactory() { this._animatorFactory = new AnimatorFactory({ animators: _animatorDict, baseClass: NavigatorAnimator, baseClassName: 'NavigatorAnimator', defaultAnimation: this.getAttribute('animation') }); } }, { key: 'disconnectedCallback', value: function disconnectedCallback() { this._backButtonHandler.destroy(); this._backButtonHandler = null; this._swipe && this._swipe.dispose(); this._swipe = null; } }, { key: 'attributeChangedCallback', value: function attributeChangedCallback(name, last, current) { switch (name) { case 'animation': this._updateAnimatorFactory(); break; case 'swipeable': this._swipe && this._swipe.update(); break; } } /** * @method popPage * @signature popPage([options]) * @param {Object} [options] * [en]Parameter object.[/en] * [ja]オプションを指定するオブジェクト。[/ja] * @param {String} [options.animation] * [en] * Animation name. Available animations are `"slide"`, `"lift"`, `"fade"` and `"none"`. * * These are platform based animations. For fixed animations, add `"-ios"` or `"-md"` suffix to the animation name. E.g. `"lift-ios"`, `"lift-md"`. Defaults values are `"slide-ios"` and `"fade-md"`. * [/en] * [ja][/ja] * @param {String} [options.animationOptions] * [en]Specify the animation's duration, delay and timing. E.g. `{duration: 0.2, delay: 0.4, timing: 'ease-in'}`.[/en] * [ja]アニメーション時のduration, delay, timingを指定します。e.g. {duration: 0.2, delay: 0.4, timing: 'ease-in'}[/ja] * @param {Function} [options.callback] * [en]Function that is called when the transition has ended.[/en] * [ja]このメソッドによる画面遷移が終了した際に呼び出される関数オブジェクトを指定します。[/ja] * @param {Object} [options.data] * [en]Custom data that will be stored in the new page element.[/en] * [ja][/ja] * @param {Number} [options.times] * [en]Number of pages to be popped. Only one animation will be shown.[/en] * [ja][/ja] * @return {Promise} * [en]Promise which resolves to the revealed page.[/en] * [ja]明らかにしたページを解決するPromiseを返します。[/ja] * @description * [en]Pops the current page from the page stack. The previous page will be displayed.[/en] * [ja]現在表示中のページをページスタックから取り除きます。一つ前のページに戻ります。[/ja] */ }, { key: 'popPage', value: function popPage() { var _this3 = this; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _preparePageAndOption = this._preparePageAndOptions(null, options); options = _preparePageAndOption.options; if (util.isInteger(options.times) && options.times > 1) { this._removePages(options.times); } var popUpdate = function popUpdate() { return new _Promise(function (resolve) { _this3._pageLoader.unload(_this3.pages[_this3.pages.length - 1]); resolve(); }); }; return this._popPage(options, popUpdate); } }, { key: '_popPage', value: function _popPage(options) { var _this4 = this; var update = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () { return _Promise.resolve(); }; if (this._isRunning) { return _Promise.reject('popPage is already running.'); } if (this.pages.length <= 1) { return _Promise.reject('ons-navigator\'s page stack is empty.'); } if (this._emitPrePopEvent()) { return _Promise.reject('Canceled in prepop event.'); } var length = this.pages.length; this._isRunning = true; this.pages[length - 2].updateBackButton(length - 2 > 0); return new _Promise(function (resolve) { var leavePage = _this4.pages[length - 1]; var enterPage = _this4.pages[length - 2]; options = util.extend({}, _this4.options || {}, leavePage.pushedOptions || {}, options); if (options.data) { enterPage.data = util.extend({}, enterPage.data || {}, options.data || {}); } var done = function done() { update().then(function () { _this4._isRunning = false; enterPage._show(); util.triggerElementEvent(_this4, 'postpop', { leavePage: leavePage, enterPage: enterPage, navigator: _this4 }); options.callback && options.callback(enterPage); resolve(enterPage); }); }; leavePage._hide(); enterPage.style.display = ''; var animator = options.animator || _this4._animatorFactory.newAnimator(options); animator.pop(_this4.pages[length - 2], _this4.pages[length - 1], done); }).catch(function () { return _this4._isRunning = false; }); } /** * @method pushPage * @signature pushPage(page, [options]) * @param {String} page * [en]Page URL. Can be either a HTML document or a template defined with the `