Brak opisu

index.js 43KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. import _typeof from 'babel-runtime/helpers/typeof';
  2. import _setImmediate from 'babel-runtime/core-js/set-immediate';
  3. import _Promise from 'babel-runtime/core-js/promise';
  4. import _WeakMap from 'babel-runtime/core-js/weak-map';
  5. import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of';
  6. import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
  7. import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
  8. import _createClass from 'babel-runtime/helpers/createClass';
  9. import _inherits from 'babel-runtime/helpers/inherits';
  10. /*
  11. Copyright 2013-2015 ASIAL CORPORATION
  12. Licensed under the Apache License, Version 2.0 (the "License");
  13. you may not use this file except in compliance with the License.
  14. You may obtain a copy of the License at
  15. http://www.apache.org/licenses/LICENSE-2.0
  16. Unless required by applicable law or agreed to in writing, software
  17. distributed under the License is distributed on an "AS IS" BASIS,
  18. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. See the License for the specific language governing permissions and
  20. limitations under the License.
  21. */
  22. import onsElements from '../../ons/elements';
  23. import util from '../../ons/util';
  24. import internal from '../../ons/internal';
  25. import SwipeReveal from '../../ons/internal/swipe-reveal';
  26. import AnimatorFactory from '../../ons/internal/animator-factory';
  27. import NavigatorAnimator from './animator';
  28. import IOSSlideNavigatorAnimator from './ios-slide-animator';
  29. import IOSLiftNavigatorAnimator from './ios-lift-animator';
  30. import IOSFadeNavigatorAnimator from './ios-fade-animator';
  31. import MDSlideNavigatorAnimator from './md-slide-animator';
  32. import MDLiftNavigatorAnimator from './md-lift-animator';
  33. import MDFadeNavigatorAnimator from './md-fade-animator';
  34. import NoneNavigatorAnimator from './none-animator';
  35. import platform from '../../ons/platform';
  36. import contentReady from '../../ons/content-ready';
  37. import BaseElement from '../base/base-element';
  38. import deviceBackButtonDispatcher from '../../ons/internal/device-back-button-dispatcher';
  39. import { PageLoader, defaultPageLoader, instantPageLoader } from '../../ons/page-loader';
  40. var _animatorDict = {
  41. 'default': function _default() {
  42. return platform.isAndroid() ? MDFadeNavigatorAnimator : IOSSlideNavigatorAnimator;
  43. },
  44. 'slide': function slide() {
  45. return platform.isAndroid() ? MDSlideNavigatorAnimator : IOSSlideNavigatorAnimator;
  46. },
  47. 'lift': function lift() {
  48. return platform.isAndroid() ? MDLiftNavigatorAnimator : IOSLiftNavigatorAnimator;
  49. },
  50. 'fade': function fade() {
  51. return platform.isAndroid() ? MDFadeNavigatorAnimator : IOSFadeNavigatorAnimator;
  52. },
  53. 'slide-ios': IOSSlideNavigatorAnimator,
  54. 'slide-md': MDSlideNavigatorAnimator,
  55. 'lift-ios': IOSLiftNavigatorAnimator,
  56. 'lift-md': MDLiftNavigatorAnimator,
  57. 'fade-ios': IOSFadeNavigatorAnimator,
  58. 'fade-md': MDFadeNavigatorAnimator,
  59. 'none': NoneNavigatorAnimator
  60. };
  61. var rewritables = {
  62. /**
  63. * @param {Element} navigatorSideElement
  64. * @param {Function} callback
  65. */
  66. ready: function ready(navigatorElement, callback) {
  67. callback();
  68. }
  69. };
  70. var verifyPageElement = function verifyPageElement(el) {
  71. return el.nodeName !== 'ONS-PAGE' && util.throw('Only page elements can be children of navigator');
  72. };
  73. /**
  74. * @element ons-navigator
  75. * @category navigation
  76. * @description
  77. * [en]
  78. * A component that provides page stack management and navigation. Stack navigation is the most common navigation pattern for mobile apps.
  79. *
  80. * 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.
  81. * [/en]
  82. * [ja][/ja]
  83. * @codepen yrhtv
  84. * @tutorial vanilla/Reference/navigator
  85. * @guide lifecycle.html#events
  86. * [en]Overview of page events[/en]
  87. * [ja]Overview of page events[/ja]
  88. * @seealso ons-toolbar
  89. * [en]The `<ons-toolbar>` component is used to display a toolbar on the top of a page.[/en]
  90. * [ja][/ja]
  91. * @seealso ons-back-button
  92. * [en]The `<ons-back-button>` component lets the user return to the previous page.[/en]
  93. * [ja][/ja]
  94. * @example
  95. * <ons-navigator id="navigator">
  96. * <ons-page>
  97. * <ons-toolbar>
  98. * <div class="center">
  99. * Title
  100. * </div>
  101. * </ons-toolbar>
  102. * <p>
  103. * <ons-button
  104. * onclick="document.getElementById('navigator').pushPage('page.html')">
  105. * Push page
  106. * </ons-button>
  107. * </p>
  108. * </ons-page>
  109. * </ons-navigator>
  110. *
  111. * <template id="page.html">
  112. * <ons-page>
  113. * <ons-toolbar>
  114. * <div class="left">
  115. * <ons-back-button>Back</ons-back-button>
  116. * </div>
  117. * <div class="center">
  118. * Another page
  119. * </div>
  120. * </ons-toolbar>
  121. * </ons-page>
  122. * </template>
  123. */
  124. var NavigatorElement = function (_BaseElement) {
  125. _inherits(NavigatorElement, _BaseElement);
  126. _createClass(NavigatorElement, [{
  127. key: 'animatorFactory',
  128. /**
  129. * @attribute page
  130. * @initonly
  131. * @type {String}
  132. * @description
  133. * [en]First page to show when navigator is initialized.[/en]
  134. * [ja]ナビゲーターが初期化された時に表示するページを指定します。[/ja]
  135. */
  136. /**
  137. * @attribute swipeable
  138. * @type {Boolean}
  139. * @description
  140. * [en]Enable iOS "swipe to pop" feature.[/en]
  141. * [ja][/ja]
  142. */
  143. /**
  144. * @attribute swipe-target-width
  145. * @type {String}
  146. * @default 20px
  147. * @description
  148. * [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]
  149. * [ja]スワイプの判定領域をピクセル単位で指定します。画面の端から指定した距離に達するとページが表示されます。[/ja]
  150. */
  151. /**
  152. * @attribute swipe-threshold
  153. * @type {Number}
  154. * @default 0.2
  155. * @description
  156. * [en]Specify how much the page needs to be swiped before popping. A value between `0` and `1`.[/en]
  157. * [ja][/ja]
  158. */
  159. /**
  160. * @attribute animation
  161. * @type {String}
  162. * @default default
  163. * @description
  164. * [en]
  165. * Animation name. Available animations are `"slide"`, `"lift"`, `"fade"` and `"none"`.
  166. *
  167. * 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.
  168. * [/en]
  169. * [ja][/ja]
  170. */
  171. /**
  172. * @attribute animation-options
  173. * @type {Expression}
  174. * @description
  175. * [en]Specify the animation's duration, timing and delay with an object literal. E.g. `{duration: 0.2, delay: 1, timing: 'ease-in'}`[/en]
  176. * [ja]アニメーション時のduration, timing, delayをオブジェクトリテラルで指定します。e.g. `{duration: 0.2, delay: 1, timing: 'ease-in'}`[/ja]
  177. */
  178. /**
  179. * @event prepush
  180. * @description
  181. * [en]Fired just before a page is pushed.[/en]
  182. * [ja]pageがpushされる直前に発火されます。[/ja]
  183. * @param {Object} event [en]Event object.[/en]
  184. * @param {Object} event.navigator
  185. * [en]Component object.[/en]
  186. * [ja]コンポーネントのオブジェクト。[/ja]
  187. * @param {Object} event.currentPage
  188. * [en]Current page object.[/en]
  189. * [ja]現在のpageオブジェクト。[/ja]
  190. * @param {Function} event.cancel
  191. * [en]Call this function to cancel the push.[/en]
  192. * [ja]この関数を呼び出すと、push処理がキャンセルされます。[/ja]
  193. */
  194. /**
  195. * @event prepop
  196. * @description
  197. * [en]Fired just before a page is popped.[/en]
  198. * [ja]pageがpopされる直前に発火されます。[/ja]
  199. * @param {Object} event [en]Event object.[/en]
  200. * @param {Object} event.navigator
  201. * [en]Component object.[/en]
  202. * [ja]コンポーネントのオブジェクト。[/ja]
  203. * @param {Object} event.currentPage
  204. * [en]Current page object.[/en]
  205. * [ja]現在のpageオブジェクト。[/ja]
  206. * @param {Function} event.cancel
  207. * [en]Call this function to cancel the pop.[/en]
  208. * [ja]この関数を呼び出すと、pageのpopがキャンセルされます。[/ja]
  209. */
  210. /**
  211. * @event postpush
  212. * @description
  213. * [en]Fired just after a page is pushed.[/en]
  214. * [ja]pageがpushされてアニメーションが終了してから発火されます。[/ja]
  215. * @param {Object} event [en]Event object.[/en]
  216. * @param {Object} event.navigator
  217. * [en]Component object.[/en]
  218. * [ja]コンポーネントのオブジェクト。[/ja]
  219. * @param {Object} event.enterPage
  220. * [en]Object of the next page.[/en]
  221. * [ja]pushされたpageオブジェクト。[/ja]
  222. * @param {Object} event.leavePage
  223. * [en]Object of the previous page.[/en]
  224. * [ja]以前のpageオブジェクト。[/ja]
  225. */
  226. /**
  227. * @event postpop
  228. * @description
  229. * [en]Fired just after a page is popped.[/en]
  230. * [ja]pageがpopされてアニメーションが終わった後に発火されます。[/ja]
  231. * @param {Object} event [en]Event object.[/en]
  232. * @param {Object} event.navigator
  233. * [en]Component object.[/en]
  234. * [ja]コンポーネントのオブジェクト。[/ja]
  235. * @param {Object} event.enterPage
  236. * [en]Object of the next page.[/en]
  237. * [ja]popされて表示されるページのオブジェクト。[/ja]
  238. * @param {Object} event.leavePage
  239. * [en]Object of the previous page.[/en]
  240. * [ja]popされて消えるページのオブジェクト。[/ja]
  241. */
  242. get: function get() {
  243. return this._animatorFactory;
  244. }
  245. }]);
  246. function NavigatorElement() {
  247. _classCallCheck(this, NavigatorElement);
  248. var _this = _possibleConstructorReturn(this, (NavigatorElement.__proto__ || _Object$getPrototypeOf(NavigatorElement)).call(this));
  249. _this._isRunning = false;
  250. _this._initialized = false;
  251. _this._pageLoader = defaultPageLoader;
  252. _this._pageMap = new _WeakMap();
  253. _this._updateAnimatorFactory();
  254. return _this;
  255. }
  256. /**
  257. * @property pageLoader
  258. * @type {PageLoader}
  259. * @description
  260. * [en]PageLoader instance. It can be overriden to change the way pages are loaded by this element. Useful for lib developers.[/en]
  261. * [ja]PageLoaderインスタンスを格納しています。[/ja]
  262. */
  263. _createClass(NavigatorElement, [{
  264. key: '_getPageTarget',
  265. value: function _getPageTarget() {
  266. return this._page || this.getAttribute('page');
  267. }
  268. /**
  269. * @property page
  270. * @type {*}
  271. * @description
  272. * [en]Specify the page to be loaded during initialization. This value takes precedence over the `page` attribute. Useful for lib developers.[/en]
  273. * [ja]初期化時に読み込むページを指定します。`page`属性で指定した値よりも`page`プロパティに指定した値を優先します。[/ja]
  274. */
  275. }, {
  276. key: 'connectedCallback',
  277. value: function connectedCallback() {
  278. var _this2 = this;
  279. this.onDeviceBackButton = this._onDeviceBackButton.bind(this);
  280. if (!platform.isAndroid() || this.getAttribute('swipeable') === 'force') {
  281. var swipeAnimator = void 0;
  282. this._swipe = new SwipeReveal({
  283. element: this,
  284. getThreshold: function getThreshold() {
  285. return Math.max(0.2, parseFloat(_this2.getAttribute('swipe-threshold')) || 0);
  286. },
  287. swipeMax: function swipeMax() {
  288. _this2._onSwipe && _this2._onSwipe(1, { duration: swipeAnimator.durationSwipe, timing: swipeAnimator.timingSwipe });
  289. _this2[_this2.swipeMax ? 'swipeMax' : 'popPage']({ animator: swipeAnimator });
  290. swipeAnimator = null;
  291. },
  292. swipeMid: function swipeMid(distance, width) {
  293. _this2._onSwipe && _this2._onSwipe(distance / width);
  294. swipeAnimator.translate(distance, width, _this2.topPage.previousElementSibling, _this2.topPage);
  295. },
  296. swipeMin: function swipeMin() {
  297. _this2._onSwipe && _this2._onSwipe(0, { duration: swipeAnimator.durationRestore, timing: swipeAnimator.timingSwipe });
  298. swipeAnimator.restore(_this2.topPage.previousElementSibling, _this2.topPage);
  299. swipeAnimator = null;
  300. },
  301. ignoreSwipe: function ignoreSwipe(event, distance) {
  302. // Basic conditions
  303. if (!_this2._isRunning && _this2.children.length > 1) {
  304. // Area or directional issues
  305. var area = parseInt(_this2.getAttribute('swipe-target-width') || 25, 10);
  306. if (event.gesture.direction === 'right' && area > distance) {
  307. // Swipes on ons-back-button and its children
  308. var isBB = function isBB(el) {
  309. return (/ons-back-button/i.test(el.tagName)
  310. );
  311. };
  312. if (!isBB(event.target) && !util.findParent(event.target, isBB, function (p) {
  313. return (/ons-page/i.test(p.tagName)
  314. );
  315. })) {
  316. // Animator is swipeable
  317. var animation = (_this2.topPage.pushedOptions || {}).animation || _this2.animatorFactory._animation;
  318. var Animator = _animatorDict[animation] instanceof Function ? _animatorDict[animation].call() : _animatorDict[animation];
  319. if (typeof Animator !== 'undefined' && Animator.swipeable) {
  320. swipeAnimator = new Animator(); // Prepare for the swipe
  321. return false;
  322. }
  323. }
  324. }
  325. }
  326. return true; // Ignore swipe
  327. }
  328. });
  329. this.attributeChangedCallback('swipeable');
  330. }
  331. if (this._initialized) {
  332. return;
  333. }
  334. this._initialized = true;
  335. var deferred = util.defer();
  336. this.loaded = deferred.promise;
  337. rewritables.ready(this, function () {
  338. var show = !util.hasAnyComponentAsParent(_this2);
  339. var options = { animation: 'none', show: show };
  340. if (_this2.pages.length === 0 && _this2._getPageTarget()) {
  341. _this2.pushPage(_this2._getPageTarget(), options).then(function () {
  342. return deferred.resolve();
  343. });
  344. } else if (_this2.pages.length > 0) {
  345. for (var i = 0; i < _this2.pages.length; i++) {
  346. verifyPageElement(_this2.pages[i]);
  347. }
  348. if (_this2.topPage) {
  349. contentReady(_this2.topPage, function () {
  350. return setTimeout(function () {
  351. deferred.resolve();
  352. show && _this2.topPage._show();
  353. _this2._updateLastPageBackButton();
  354. }, 0);
  355. });
  356. }
  357. } else {
  358. contentReady(_this2, function () {
  359. if (_this2.pages.length === 0 && _this2._getPageTarget()) {
  360. _this2.pushPage(_this2._getPageTarget(), options).then(function () {
  361. return deferred.resolve();
  362. });
  363. } else {
  364. deferred.resolve();
  365. }
  366. });
  367. }
  368. });
  369. }
  370. }, {
  371. key: '_updateAnimatorFactory',
  372. value: function _updateAnimatorFactory() {
  373. this._animatorFactory = new AnimatorFactory({
  374. animators: _animatorDict,
  375. baseClass: NavigatorAnimator,
  376. baseClassName: 'NavigatorAnimator',
  377. defaultAnimation: this.getAttribute('animation')
  378. });
  379. }
  380. }, {
  381. key: 'disconnectedCallback',
  382. value: function disconnectedCallback() {
  383. this._backButtonHandler.destroy();
  384. this._backButtonHandler = null;
  385. this._swipe && this._swipe.dispose();
  386. this._swipe = null;
  387. }
  388. }, {
  389. key: 'attributeChangedCallback',
  390. value: function attributeChangedCallback(name, last, current) {
  391. switch (name) {
  392. case 'animation':
  393. this._updateAnimatorFactory();
  394. break;
  395. case 'swipeable':
  396. this._swipe && this._swipe.update();
  397. break;
  398. }
  399. }
  400. /**
  401. * @method popPage
  402. * @signature popPage([options])
  403. * @param {Object} [options]
  404. * [en]Parameter object.[/en]
  405. * [ja]オプションを指定するオブジェクト。[/ja]
  406. * @param {String} [options.animation]
  407. * [en]
  408. * Animation name. Available animations are `"slide"`, `"lift"`, `"fade"` and `"none"`.
  409. *
  410. * 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"`.
  411. * [/en]
  412. * [ja][/ja]
  413. * @param {String} [options.animationOptions]
  414. * [en]Specify the animation's duration, delay and timing. E.g. `{duration: 0.2, delay: 0.4, timing: 'ease-in'}`.[/en]
  415. * [ja]アニメーション時のduration, delay, timingを指定します。e.g. {duration: 0.2, delay: 0.4, timing: 'ease-in'}[/ja]
  416. * @param {Function} [options.callback]
  417. * [en]Function that is called when the transition has ended.[/en]
  418. * [ja]このメソッドによる画面遷移が終了した際に呼び出される関数オブジェクトを指定します。[/ja]
  419. * @param {Object} [options.data]
  420. * [en]Custom data that will be stored in the new page element.[/en]
  421. * [ja][/ja]
  422. * @param {Number} [options.times]
  423. * [en]Number of pages to be popped. Only one animation will be shown.[/en]
  424. * [ja][/ja]
  425. * @return {Promise}
  426. * [en]Promise which resolves to the revealed page.[/en]
  427. * [ja]明らかにしたページを解決するPromiseを返します。[/ja]
  428. * @description
  429. * [en]Pops the current page from the page stack. The previous page will be displayed.[/en]
  430. * [ja]現在表示中のページをページスタックから取り除きます。一つ前のページに戻ります。[/ja]
  431. */
  432. }, {
  433. key: 'popPage',
  434. value: function popPage() {
  435. var _this3 = this;
  436. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  437. var _preparePageAndOption = this._preparePageAndOptions(null, options);
  438. options = _preparePageAndOption.options;
  439. if (util.isInteger(options.times) && options.times > 1) {
  440. this._removePages(options.times);
  441. }
  442. var popUpdate = function popUpdate() {
  443. return new _Promise(function (resolve) {
  444. _this3._pageLoader.unload(_this3.pages[_this3.pages.length - 1]);
  445. resolve();
  446. });
  447. };
  448. return this._popPage(options, popUpdate);
  449. }
  450. }, {
  451. key: '_popPage',
  452. value: function _popPage(options) {
  453. var _this4 = this;
  454. var update = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
  455. return _Promise.resolve();
  456. };
  457. if (this._isRunning) {
  458. return _Promise.reject('popPage is already running.');
  459. }
  460. if (this.pages.length <= 1) {
  461. return _Promise.reject('ons-navigator\'s page stack is empty.');
  462. }
  463. if (this._emitPrePopEvent()) {
  464. return _Promise.reject('Canceled in prepop event.');
  465. }
  466. var length = this.pages.length;
  467. this._isRunning = true;
  468. this.pages[length - 2].updateBackButton(length - 2 > 0);
  469. return new _Promise(function (resolve) {
  470. var leavePage = _this4.pages[length - 1];
  471. var enterPage = _this4.pages[length - 2];
  472. options = util.extend({}, _this4.options || {}, leavePage.pushedOptions || {}, options);
  473. if (options.data) {
  474. enterPage.data = util.extend({}, enterPage.data || {}, options.data || {});
  475. }
  476. var done = function done() {
  477. update().then(function () {
  478. _this4._isRunning = false;
  479. enterPage._show();
  480. util.triggerElementEvent(_this4, 'postpop', { leavePage: leavePage, enterPage: enterPage, navigator: _this4 });
  481. options.callback && options.callback(enterPage);
  482. resolve(enterPage);
  483. });
  484. };
  485. leavePage._hide();
  486. enterPage.style.display = '';
  487. var animator = options.animator || _this4._animatorFactory.newAnimator(options);
  488. animator.pop(_this4.pages[length - 2], _this4.pages[length - 1], done);
  489. }).catch(function () {
  490. return _this4._isRunning = false;
  491. });
  492. }
  493. /**
  494. * @method pushPage
  495. * @signature pushPage(page, [options])
  496. * @param {String} page
  497. * [en]Page URL. Can be either a HTML document or a template defined with the `<template>` tag.[/en]
  498. * [ja]pageのURLか、もしくは`<template>`で宣言したテンプレートのid属性の値を指定できます。[/ja]
  499. * @param {Object} [options]
  500. * [en]Parameter object.[/en]
  501. * [ja]オプションを指定するオブジェクト。[/ja]
  502. * @param {String} [options.page]
  503. * [en]Page URL. Only necessary if `page` parameter is null or undefined.[/en]
  504. * [ja][/ja]
  505. * @param {String} [options.pageHTML]
  506. * [en]HTML code that will be computed as a new page. Overwrites `page` parameter.[/en]
  507. * [ja][/ja]
  508. * @param {String} [options.animation]
  509. * [en]
  510. * Animation name. Available animations are `"slide"`, `"lift"`, `"fade"` and `"none"`.
  511. *
  512. * 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"`.
  513. * [/en]
  514. * [ja][/ja]
  515. * @param {String} [options.animationOptions]
  516. * [en]Specify the animation's duration, delay and timing. E.g. `{duration: 0.2, delay: 0.4, timing: 'ease-in'}`[/en]
  517. * [ja]アニメーション時のduration, delay, timingを指定します。e.g. `{duration: 0.2, delay: 0.4, timing: 'ease-in'}` [/ja]
  518. * @param {Function} [options.callback]
  519. * [en]Function that is called when the transition has ended.[/en]
  520. * [ja]pushPage()による画面遷移が終了した時に呼び出される関数オブジェクトを指定します。[/ja]
  521. * @param {Object} [options.data]
  522. * [en]Custom data that will be stored in the new page element.[/en]
  523. * [ja][/ja]
  524. * @return {Promise}
  525. * [en]Promise which resolves to the pushed page.[/en]
  526. * [ja]追加したページを解決するPromiseを返します。[/ja]
  527. * @description
  528. * [en]Pushes the specified page into the stack.[/en]
  529. * [ja]指定したpageを新しいページスタックに追加します。新しいページが表示されます。[/ja]
  530. */
  531. }, {
  532. key: 'pushPage',
  533. value: function pushPage(page) {
  534. var _this5 = this;
  535. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  536. var _preparePageAndOption2 = this._preparePageAndOptions(page, options);
  537. page = _preparePageAndOption2.page;
  538. options = _preparePageAndOption2.options;
  539. var prepare = function prepare(pageElement) {
  540. verifyPageElement(pageElement);
  541. _this5._pageMap.set(pageElement, page);
  542. pageElement = util.extend(pageElement, {
  543. data: options.data
  544. });
  545. pageElement.style.visibility = 'hidden';
  546. };
  547. if (options.pageHTML) {
  548. return this._pushPage(options, function () {
  549. return new _Promise(function (resolve) {
  550. instantPageLoader.load({ page: options.pageHTML, parent: _this5, params: options.data }, function (pageElement) {
  551. prepare(pageElement);
  552. resolve();
  553. });
  554. });
  555. });
  556. }
  557. return this._pushPage(options, function () {
  558. return new _Promise(function (resolve) {
  559. _this5._pageLoader.load({ page: page, parent: _this5, params: options.data }, function (pageElement) {
  560. prepare(pageElement);
  561. resolve();
  562. });
  563. });
  564. });
  565. }
  566. }, {
  567. key: '_pushPage',
  568. value: function _pushPage() {
  569. var _this6 = this;
  570. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  571. var update = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
  572. return _Promise.resolve();
  573. };
  574. if (this._isRunning) {
  575. return _Promise.reject('pushPage is already running.');
  576. }
  577. if (this._emitPrePushEvent()) {
  578. return _Promise.reject('Canceled in prepush event.');
  579. }
  580. this._isRunning = true;
  581. var animationOptions = AnimatorFactory.parseAnimationOptionsString(this.getAttribute('animation-options'));
  582. options = util.extend({}, this.options || {}, { animationOptions: animationOptions }, options);
  583. var animator = this._animatorFactory.newAnimator(options);
  584. return update().then(function () {
  585. var pageLength = _this6.pages.length;
  586. var enterPage = _this6.pages[pageLength - 1];
  587. var leavePage = options.leavePage || _this6.pages[pageLength - 2];
  588. verifyPageElement(enterPage);
  589. enterPage.updateBackButton(pageLength > (options._replacePage ? 2 : 1));
  590. enterPage.pushedOptions = util.extend({}, enterPage.pushedOptions || {}, options || {});
  591. enterPage.data = util.extend({}, enterPage.data || {}, options.data || {});
  592. enterPage.unload = enterPage.unload || options.unload;
  593. return new _Promise(function (resolve) {
  594. var done = function done() {
  595. _this6._isRunning = false;
  596. options.show !== false && _setImmediate(function () {
  597. return enterPage._show();
  598. });
  599. util.triggerElementEvent(_this6, 'postpush', { leavePage: leavePage, enterPage: enterPage, navigator: _this6 });
  600. if (leavePage) {
  601. leavePage.style.display = 'none';
  602. }
  603. options.callback && options.callback(enterPage);
  604. resolve(enterPage);
  605. };
  606. enterPage.style.visibility = '';
  607. if (leavePage) {
  608. leavePage._hide();
  609. animator.push(enterPage, leavePage, done);
  610. } else {
  611. done();
  612. }
  613. });
  614. }).catch(function (error) {
  615. _this6._isRunning = false;
  616. throw error;
  617. });
  618. }
  619. /**
  620. * @method replacePage
  621. * @signature replacePage(page, [options])
  622. * @return {Promise}
  623. * [en]Promise which resolves to the new page.[/en]
  624. * [ja]新しいページを解決するPromiseを返します。[/ja]
  625. * @description
  626. * [en]Replaces the current top page with the specified one. Extends `pushPage()` parameters.[/en]
  627. * [ja]現在表示中のページをを指定したページに置き換えます。[/ja]
  628. */
  629. }, {
  630. key: 'replacePage',
  631. value: function replacePage(page) {
  632. var _this7 = this;
  633. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  634. return this.pushPage(page, options).then(function (resolvedValue) {
  635. if (_this7.pages.length > 1) {
  636. _this7._pageLoader.unload(_this7.pages[_this7.pages.length - 2]);
  637. }
  638. _this7._updateLastPageBackButton();
  639. return _Promise.resolve(resolvedValue);
  640. });
  641. }
  642. /**
  643. * @method insertPage
  644. * @signature insertPage(index, page, [options])
  645. * @param {Number} index
  646. * [en]The index where it should be inserted.[/en]
  647. * [ja]スタックに挿入する位置のインデックスを指定します。[/ja]
  648. * @return {Promise}
  649. * [en]Promise which resolves to the inserted page.[/en]
  650. * [ja]指定したページを解決するPromiseを返します。[/ja]
  651. * @description
  652. * [en]Insert the specified page into the stack with at a position defined by the `index` argument. Extends `pushPage()` parameters.[/en]
  653. * [ja]指定したpageをページスタックのindexで指定した位置に追加します。[/ja]
  654. */
  655. }, {
  656. key: 'insertPage',
  657. value: function insertPage(index, page) {
  658. var _this8 = this;
  659. var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  660. var _preparePageAndOption3 = this._preparePageAndOptions(page, options);
  661. page = _preparePageAndOption3.page;
  662. options = _preparePageAndOption3.options;
  663. index = this._normalizeIndex(index);
  664. if (index >= this.pages.length) {
  665. return this.pushPage(page, options);
  666. }
  667. page = typeof options.pageHTML === 'string' ? options.pageHTML : page;
  668. var loader = typeof options.pageHTML === 'string' ? instantPageLoader : this._pageLoader;
  669. return new _Promise(function (resolve) {
  670. loader.load({ page: page, parent: _this8 }, function (pageElement) {
  671. verifyPageElement(pageElement);
  672. _this8._pageMap.set(pageElement, page);
  673. pageElement = util.extend(pageElement, {
  674. data: options.data,
  675. pushedOptions: options
  676. });
  677. options.animationOptions = util.extend({}, AnimatorFactory.parseAnimationOptionsString(_this8.getAttribute('animation-options')), options.animationOptions || {});
  678. pageElement.style.display = 'none';
  679. _this8.insertBefore(pageElement, _this8.pages[index]);
  680. _this8.topPage.updateBackButton(true);
  681. setTimeout(function () {
  682. pageElement = null;
  683. resolve(_this8.pages[index]);
  684. }, 1000 / 60);
  685. });
  686. });
  687. }
  688. /**
  689. * @method removePage
  690. * @signature removePage(index, [options])
  691. * @param {Number} index
  692. * [en]The index where it should be removed.[/en]
  693. * [ja]スタックから削除するページのインデックスを指定します。[/ja]
  694. * @return {Promise}
  695. * [en]Promise which resolves to the revealed page.[/en]
  696. * [ja]削除によって表示されたページを解決するPromiseを返します。[/ja]
  697. * @description
  698. * [en]Remove the specified page at a position in the stack defined by the `index` argument. Extends `popPage()` parameters.[/en]
  699. * [ja]指定したインデックスにあるページを削除します。[/ja]
  700. */
  701. }, {
  702. key: 'removePage',
  703. value: function removePage(index) {
  704. var _this9 = this;
  705. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  706. index = this._normalizeIndex(index);
  707. if (index < this.pages.length - 1) {
  708. return new _Promise(function (resolve) {
  709. var leavePage = _this9.pages[index];
  710. var enterPage = _this9.topPage;
  711. _this9._pageMap.delete(leavePage);
  712. _this9._pageLoader.unload(leavePage);
  713. if (_this9.pages.length === 1) {
  714. // edge case
  715. _this9.topPage.updateBackButton(false);
  716. }
  717. resolve(enterPage);
  718. });
  719. } else {
  720. return this.popPage(options);
  721. }
  722. }
  723. /**
  724. * @method resetToPage
  725. * @signature resetToPage(page, [options])
  726. * @return {Promise}
  727. * [en]Promise which resolves to the new top page.[/en]
  728. * [ja]新しいトップページを解決するPromiseを返します。[/ja]
  729. * @param {Boolean} [options.pop]
  730. * [en]Performs 'pop' effect if `true` instead of 'push' or none. This also sets `options.animation` value to `default` instead of `none`.[/en]
  731. * [ja][/ja]
  732. * @description
  733. * [en]Clears page stack and adds the specified page to the stack. Extends `pushPage()` parameters.[/en]
  734. * [ja]ページスタックをリセットし、指定したページを表示します。[/ja]
  735. */
  736. }, {
  737. key: 'resetToPage',
  738. value: function resetToPage(page) {
  739. var _this10 = this;
  740. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  741. var _preparePageAndOption4 = this._preparePageAndOptions(page, options);
  742. page = _preparePageAndOption4.page;
  743. options = _preparePageAndOption4.options;
  744. if (!options.animator && !options.animation && !options.pop) {
  745. options.animation = 'none';
  746. }
  747. if (!options.page && !options.pageHTML && this._getPageTarget()) {
  748. page = options.page = this._getPageTarget();
  749. }
  750. if (options.pop) {
  751. this._removePages();
  752. return this.insertPage(0, page, { data: options.data }).then(function () {
  753. return _this10.popPage(options);
  754. });
  755. }
  756. // Tip: callback runs before resolved promise
  757. var callback = options.callback;
  758. options.callback = function (newPage) {
  759. _this10._removePages();
  760. newPage.updateBackButton(false);
  761. callback && callback(newPage);
  762. };
  763. return this.pushPage(page, options);
  764. }
  765. /**
  766. * @method bringPageTop
  767. * @signature bringPageTop(item, [options])
  768. * @param {String|Number} item
  769. * [en]Page URL or index of an existing page in navigator's stack.[/en]
  770. * [ja]ページのURLかもしくはons-navigatorのページスタックのインデックス値を指定します。[/ja]
  771. * @return {Promise}
  772. * [en]Promise which resolves to the new top page.[/en]
  773. * [ja]新しいトップページを解決するPromiseを返します。[/ja]
  774. * @description
  775. * [en]Brings the given page to the top of the page stack if it already exists or pushes it into the stack if doesn't. Extends `pushPage()` parameters.[/en]
  776. * [ja]指定したページをページスタックの一番上に移動します。もし指定したページが無かった場合新しくpushされます。[/ja]
  777. */
  778. }, {
  779. key: 'bringPageTop',
  780. value: function bringPageTop(item) {
  781. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  782. if (['number', 'string'].indexOf(typeof item === 'undefined' ? 'undefined' : _typeof(item)) === -1) {
  783. util.throw('First argument must be a page name or the index of an existing page. You supplied ' + item);
  784. }
  785. var index = typeof item === 'number' ? this._normalizeIndex(item) : this._lastIndexOfPage(item);
  786. var page = this.pages[index];
  787. if (index < 0) {
  788. return this.pushPage(item, options);
  789. }
  790. var _preparePageAndOption5 = this._preparePageAndOptions(page, options);
  791. options = _preparePageAndOption5.options;
  792. if (index === this.pages.length - 1) {
  793. return _Promise.resolve(page);
  794. }
  795. if (!page) {
  796. util.throw('Failed to find item ' + item);
  797. }
  798. if (this._isRunning) {
  799. return _Promise.reject('pushPage is already running.');
  800. }
  801. if (this._emitPrePushEvent()) {
  802. return _Promise.reject('Canceled in prepush event.');
  803. }
  804. page.style.display = '';
  805. page.style.visibility = 'hidden';
  806. page.parentNode.appendChild(page);
  807. return this._pushPage(options);
  808. }
  809. }, {
  810. key: '_preparePageAndOptions',
  811. value: function _preparePageAndOptions(page) {
  812. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  813. if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) != 'object') {
  814. util.throw('options must be an object. You supplied ' + options);
  815. }
  816. if ((page === null || page === undefined) && options.page) {
  817. page = options.page;
  818. }
  819. options = util.extend({}, this.options || {}, options, { page: page });
  820. return { page: page, options: options };
  821. }
  822. }, {
  823. key: '_removePages',
  824. value: function _removePages(times) {
  825. var pages = this.pages;
  826. var until = times === undefined ? 0 : pages.length - times;
  827. until = until < 0 ? 1 : until;
  828. for (var i = pages.length - 2; i >= until; i--) {
  829. this._pageMap.delete(pages[i]);
  830. this._pageLoader.unload(pages[i]);
  831. }
  832. }
  833. }, {
  834. key: '_updateLastPageBackButton',
  835. value: function _updateLastPageBackButton() {
  836. var index = this.pages.length - 1;
  837. if (index >= 0) {
  838. this.pages[index].updateBackButton(index > 0);
  839. }
  840. }
  841. }, {
  842. key: '_normalizeIndex',
  843. value: function _normalizeIndex(index) {
  844. return index >= 0 ? index : Math.abs(this.pages.length + index) % this.pages.length;
  845. }
  846. }, {
  847. key: '_onDeviceBackButton',
  848. value: function _onDeviceBackButton(event) {
  849. if (this.pages.length > 1) {
  850. this.popPage();
  851. } else {
  852. event.callParentHandler();
  853. }
  854. }
  855. }, {
  856. key: '_lastIndexOfPage',
  857. value: function _lastIndexOfPage(pageName) {
  858. var index = void 0;
  859. for (index = this.pages.length - 1; index >= 0; index--) {
  860. if (pageName === this._pageMap.get(this.pages[index])) {
  861. break;
  862. }
  863. }
  864. return index;
  865. }
  866. }, {
  867. key: '_emitPreEvent',
  868. value: function _emitPreEvent(name) {
  869. var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  870. var isCanceled = false;
  871. util.triggerElementEvent(this, 'pre' + name, util.extend({
  872. navigator: this,
  873. currentPage: this.pages[this.pages.length - 1],
  874. cancel: function cancel() {
  875. return isCanceled = true;
  876. }
  877. }, data));
  878. return isCanceled;
  879. }
  880. }, {
  881. key: '_emitPrePushEvent',
  882. value: function _emitPrePushEvent() {
  883. return this._emitPreEvent('push');
  884. }
  885. }, {
  886. key: '_emitPrePopEvent',
  887. value: function _emitPrePopEvent() {
  888. var l = this.pages.length;
  889. return this._emitPreEvent('pop', {
  890. leavePage: this.pages[l - 1],
  891. enterPage: this.pages[l - 2]
  892. });
  893. }
  894. // TODO: 書き直す
  895. }, {
  896. key: '_createPageElement',
  897. value: function _createPageElement(templateHTML) {
  898. var pageElement = util.createElement(internal.normalizePageHTML(templateHTML));
  899. verifyPageElement(pageElement);
  900. return pageElement;
  901. }
  902. /**
  903. * @property onDeviceBackButton
  904. * @type {Object}
  905. * @description
  906. * [en]Back-button handler.[/en]
  907. * [ja]バックボタンハンドラ。[/ja]
  908. */
  909. }, {
  910. key: '_show',
  911. value: function _show() {
  912. var _this11 = this;
  913. this.loaded.then(function () {
  914. return _this11.topPage && _this11.topPage._show();
  915. });
  916. }
  917. }, {
  918. key: '_hide',
  919. value: function _hide() {
  920. this.topPage && this.topPage._hide();
  921. }
  922. }, {
  923. key: '_destroy',
  924. value: function _destroy() {
  925. for (var i = this.pages.length - 1; i >= 0; i--) {
  926. this._pageLoader.unload(this.pages[i]);
  927. }
  928. this.remove();
  929. }
  930. /**
  931. * @param {String} name
  932. * @param {Function} Animator
  933. */
  934. }, {
  935. key: 'pageLoader',
  936. get: function get() {
  937. return this._pageLoader;
  938. },
  939. set: function set(pageLoader) {
  940. if (!(pageLoader instanceof PageLoader)) {
  941. util.throwPageLoader();
  942. }
  943. this._pageLoader = pageLoader;
  944. }
  945. }, {
  946. key: 'page',
  947. get: function get() {
  948. return this._page;
  949. },
  950. set: function set(page) {
  951. this._page = page;
  952. }
  953. }, {
  954. key: 'onDeviceBackButton',
  955. get: function get() {
  956. return this._backButtonHandler;
  957. },
  958. set: function set(callback) {
  959. if (this._backButtonHandler) {
  960. this._backButtonHandler.destroy();
  961. }
  962. this._backButtonHandler = deviceBackButtonDispatcher.createHandler(this, callback);
  963. }
  964. /**
  965. * @property topPage
  966. * @readonly
  967. * @type {HTMLElement}
  968. * @description
  969. * [en]Current top page element. Use this method to access options passed by `pushPage()`-like methods.[/en]
  970. * [ja]現在のページを取得します。pushPage()やresetToPage()メソッドの引数を取得できます。[/ja]
  971. */
  972. }, {
  973. key: 'topPage',
  974. get: function get() {
  975. var last = this.lastElementChild;
  976. while (last && last.tagName !== 'ONS-PAGE') {
  977. last = last.previousElementSibling;
  978. }
  979. return last;
  980. }
  981. /**
  982. * @property pages
  983. * @readonly
  984. * @type {Array}
  985. * @description
  986. * [en]Copy of the navigator's page stack.[/en]
  987. * [ja][/ja]
  988. */
  989. }, {
  990. key: 'pages',
  991. get: function get() {
  992. return util.arrayFrom(this.children).filter(function (element) {
  993. return element.tagName === 'ONS-PAGE';
  994. });
  995. }
  996. /**
  997. * @property onSwipe
  998. * @type {Function}
  999. * @description
  1000. * [en]Hook called whenever the user slides the navigator (swipe-to-pop). It gets a decimal ratio (0-1) and an animationOptions object as arguments.[/en]
  1001. * [ja][/ja]
  1002. */
  1003. }, {
  1004. key: 'onSwipe',
  1005. get: function get() {
  1006. return this._onSwipe;
  1007. },
  1008. set: function set(value) {
  1009. if (value && !(value instanceof Function)) {
  1010. util.throw('"onSwipe" must be a function');
  1011. }
  1012. this._onSwipe = value;
  1013. }
  1014. /**
  1015. * @property options
  1016. * @type {Object}
  1017. * @description
  1018. * [en]Default options object. Attributes have priority over this property.[/en]
  1019. * [ja][/ja]
  1020. */
  1021. /**
  1022. * @property options.animation
  1023. * @type {String}
  1024. * @description
  1025. * [en]
  1026. * Animation name. Available animations are `"slide"`, `"lift"`, `"fade"` and `"none"`.
  1027. * 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"`.
  1028. * [/en]
  1029. * [ja][/ja]
  1030. */
  1031. /**
  1032. * @property options.animationOptions
  1033. * @type {String}
  1034. * @description
  1035. * [en]Specify the animation's duration, delay and timing. E.g. `{duration: 0.2, delay: 0.4, timing: 'ease-in'}`.[/en]
  1036. * [ja]アニメーション時のduration, delay, timingを指定します。e.g. `{duration: 0.2, delay: 0.4, timing: 'ease-in'}` [/ja]
  1037. */
  1038. /**
  1039. * @property options.callback
  1040. * @type {String}
  1041. * @description
  1042. * [en]Function that is called when the transition has ended.[/en]
  1043. * [ja]このメソッドによる画面遷移が終了した際に呼び出される関数オブジェクトを指定します。[/ja]
  1044. */
  1045. }, {
  1046. key: 'options',
  1047. get: function get() {
  1048. return this._options;
  1049. },
  1050. set: function set(object) {
  1051. this._options = object;
  1052. }
  1053. }, {
  1054. key: '_isRunning',
  1055. set: function set(value) {
  1056. this.setAttribute('_is-running', value ? 'true' : 'false');
  1057. },
  1058. get: function get() {
  1059. return JSON.parse(this.getAttribute('_is-running'));
  1060. }
  1061. }], [{
  1062. key: 'registerAnimator',
  1063. value: function registerAnimator(name, Animator) {
  1064. if (!(Animator.prototype instanceof NavigatorAnimator)) {
  1065. util.throwAnimator('Navigator');
  1066. }
  1067. _animatorDict[name] = Animator;
  1068. }
  1069. }, {
  1070. key: 'observedAttributes',
  1071. get: function get() {
  1072. return ['animation', 'swipeable'];
  1073. }
  1074. }, {
  1075. key: 'animators',
  1076. get: function get() {
  1077. return _animatorDict;
  1078. }
  1079. }, {
  1080. key: 'NavigatorAnimator',
  1081. get: function get() {
  1082. return NavigatorAnimator;
  1083. }
  1084. }, {
  1085. key: 'events',
  1086. get: function get() {
  1087. return ['prepush', 'postpush', 'prepop', 'postpop'];
  1088. }
  1089. }, {
  1090. key: 'rewritables',
  1091. get: function get() {
  1092. return rewritables;
  1093. }
  1094. }]);
  1095. return NavigatorElement;
  1096. }(BaseElement);
  1097. export default NavigatorElement;
  1098. onsElements.Navigator = NavigatorElement;
  1099. customElements.define('ons-navigator', NavigatorElement);