UI for Zipcoin Blue

app.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. import { EventEmitter, Injectable, Optional } from '@angular/core';
  2. import { DOCUMENT, Title } from '@angular/platform-browser';
  3. import * as Constants from './app-constants';
  4. import { Config } from '../../config/config';
  5. import { DIRECTION_BACK, DIRECTION_FORWARD, isTabs } from '../../navigation/nav-util';
  6. import { MenuController } from './menu-controller';
  7. import { Platform } from '../../platform/platform';
  8. import { IOSTransition } from '../../transitions/transition-ios';
  9. import { MDTransition } from '../../transitions/transition-md';
  10. import { WPTransition } from '../../transitions/transition-wp';
  11. /**
  12. * @name App
  13. * @description
  14. * App is a utility class used in Ionic to get information about various aspects of an app
  15. */
  16. var App = (function () {
  17. function App(_config, _plt, _menuCtrl) {
  18. this._config = _config;
  19. this._plt = _plt;
  20. this._menuCtrl = _menuCtrl;
  21. this._disTime = 0;
  22. this._scrollTime = 0;
  23. this._title = '';
  24. this._titleSrv = new Title(DOCUMENT);
  25. this._rootNavs = new Map();
  26. this._didScroll = false;
  27. /**
  28. * Observable that emits whenever a view loads in the app.
  29. * @returns {Observable} Returns an observable
  30. */
  31. this.viewDidLoad = new EventEmitter();
  32. /**
  33. * Observable that emits before any view is entered in the app.
  34. * @returns {Observable} Returns an observable
  35. */
  36. this.viewWillEnter = new EventEmitter();
  37. /**
  38. * Observable that emits after any view is entered in the app.
  39. * @returns {Observable} Returns an observable
  40. */
  41. this.viewDidEnter = new EventEmitter();
  42. /**
  43. * Observable that emits before any view is exited in the app.
  44. * @returns {Observable} Returns an observable
  45. */
  46. this.viewWillLeave = new EventEmitter();
  47. /**
  48. * Observable that emits after any view is exited in the app.
  49. * @returns {Observable} Returns an observable
  50. */
  51. this.viewDidLeave = new EventEmitter();
  52. /**
  53. * Observable that emits before any view unloads in the app.
  54. * @returns {Observable} Returns an observable
  55. */
  56. this.viewWillUnload = new EventEmitter();
  57. // listen for hardware back button events
  58. // register this back button action with a default priority
  59. _plt.registerBackButtonAction(this.goBack.bind(this));
  60. this._disableScrollAssist = _config.getBoolean('disableScrollAssist', false);
  61. var blurring = _config.getBoolean('inputBlurring', false);
  62. if (blurring) {
  63. this._enableInputBlurring();
  64. }
  65. (void 0) /* runInDev */;
  66. _config.setTransition('ios-transition', IOSTransition);
  67. _config.setTransition('md-transition', MDTransition);
  68. _config.setTransition('wp-transition', WPTransition);
  69. }
  70. /**
  71. * Sets the document title.
  72. * @param {string} val Value to set the document title to.
  73. */
  74. App.prototype.setTitle = function (val) {
  75. if (val !== this._title) {
  76. this._title = val;
  77. this._titleSrv.setTitle(val);
  78. }
  79. };
  80. /**
  81. * @hidden
  82. */
  83. App.prototype.setElementClass = function (className, isAdd) {
  84. this._appRoot.setElementClass(className, isAdd);
  85. };
  86. /**
  87. * @hidden
  88. * Sets if the app is currently enabled or not, meaning if it's
  89. * available to accept new user commands. For example, this is set to `false`
  90. * while views transition, a modal slides up, an action-sheet
  91. * slides up, etc. After the transition completes it is set back to `true`.
  92. * @param {boolean} isEnabled `true` for enabled, `false` for disabled
  93. * @param {number} duration When `isEnabled` is set to `false`, this argument
  94. * is used to set the maximum number of milliseconds that app will wait until
  95. * it will automatically enable the app again. It's basically a fallback incase
  96. * something goes wrong during a transition and the app wasn't re-enabled correctly.
  97. */
  98. App.prototype.setEnabled = function (isEnabled, duration, minDuration) {
  99. if (duration === void 0) { duration = 700; }
  100. if (minDuration === void 0) { minDuration = 0; }
  101. this._disTime = (isEnabled ? 0 : Date.now() + duration);
  102. if (this._clickBlock) {
  103. if (isEnabled) {
  104. // disable the click block if it's enabled, or the duration is tiny
  105. this._clickBlock.activate(false, CLICK_BLOCK_BUFFER_IN_MILLIS, minDuration);
  106. }
  107. else {
  108. // show the click block for duration + some number
  109. this._clickBlock.activate(true, duration + CLICK_BLOCK_BUFFER_IN_MILLIS, minDuration);
  110. }
  111. }
  112. };
  113. /**
  114. * @hidden
  115. * Toggles whether an application can be scrolled
  116. * @param {boolean} disableScroll when set to `false`, the application's
  117. * scrolling is enabled. When set to `true`, scrolling is disabled.
  118. */
  119. App.prototype._setDisableScroll = function (disableScroll) {
  120. if (this._disableScrollAssist) {
  121. this._appRoot._disableScroll(disableScroll);
  122. }
  123. };
  124. /**
  125. * @hidden
  126. * Boolean if the app is actively enabled or not.
  127. * @return {boolean}
  128. */
  129. App.prototype.isEnabled = function () {
  130. var disTime = this._disTime;
  131. if (disTime === 0) {
  132. return true;
  133. }
  134. return (disTime < Date.now());
  135. };
  136. /**
  137. * @hidden
  138. */
  139. App.prototype.setScrolling = function () {
  140. this._scrollTime = Date.now() + ACTIVE_SCROLLING_TIME;
  141. this._didScroll = true;
  142. };
  143. /**
  144. * Boolean if the app is actively scrolling or not.
  145. * @return {boolean} returns true or false
  146. */
  147. App.prototype.isScrolling = function () {
  148. var scrollTime = this._scrollTime;
  149. if (scrollTime === 0) {
  150. return false;
  151. }
  152. if (scrollTime < Date.now()) {
  153. this._scrollTime = 0;
  154. return false;
  155. }
  156. return true;
  157. };
  158. /**
  159. * @return {NavController} Returns the first Active Nav Controller from the list. This method is deprecated
  160. */
  161. App.prototype.getActiveNav = function () {
  162. console.warn('(getActiveNav) is deprecated and will be removed in the next major release. Use getActiveNavs instead.');
  163. var navs = this.getActiveNavs();
  164. if (navs && navs.length) {
  165. return navs[0];
  166. }
  167. return null;
  168. };
  169. /**
  170. * @return {NavController[]} Returns the active NavControllers. Using this method is preferred when we need access to the top-level navigation controller while on the outside views and handlers like `registerBackButtonAction()`
  171. */
  172. App.prototype.getActiveNavs = function (rootNavId) {
  173. var portal = this._appRoot._getPortal(Constants.PORTAL_MODAL);
  174. if (portal.length() > 0) {
  175. return findTopNavs(portal);
  176. }
  177. if (!this._rootNavs || !this._rootNavs.size) {
  178. return [];
  179. }
  180. if (this._rootNavs.size === 1) {
  181. return findTopNavs(this._rootNavs.values().next().value);
  182. }
  183. if (rootNavId) {
  184. return findTopNavs(this._rootNavs.get(rootNavId));
  185. }
  186. // fallback to just using all root names
  187. var activeNavs = [];
  188. this._rootNavs.forEach(function (nav) {
  189. var topNavs = findTopNavs(nav);
  190. activeNavs = activeNavs.concat(topNavs);
  191. });
  192. return activeNavs;
  193. };
  194. App.prototype.getRootNav = function () {
  195. console.warn('(getRootNav) is deprecated and will be removed in the next major release. Use getRootNavById instead.');
  196. var rootNavs = this.getRootNavs();
  197. if (rootNavs.length === 0) {
  198. return null;
  199. }
  200. else if (rootNavs.length > 1) {
  201. console.warn('(getRootNav) there are multiple root navs, use getRootNavs instead');
  202. }
  203. return rootNavs[0];
  204. };
  205. App.prototype.getRootNavs = function () {
  206. var navs = [];
  207. this._rootNavs.forEach(function (nav) { return navs.push(nav); });
  208. return navs;
  209. };
  210. /**
  211. * @return {NavController} Returns the root NavController
  212. */
  213. App.prototype.getRootNavById = function (navId) {
  214. return this._rootNavs.get(navId);
  215. };
  216. /**
  217. * @hidden
  218. */
  219. App.prototype.registerRootNav = function (nav) {
  220. this._rootNavs.set(nav.id, nav);
  221. };
  222. /**
  223. * @hidden
  224. */
  225. App.prototype.unregisterRootNav = function (nav) {
  226. this._rootNavs.delete(nav.id);
  227. };
  228. App.prototype.getActiveNavContainers = function () {
  229. // for each root nav container, get it's active nav
  230. var list = [];
  231. this._rootNavs.forEach(function (container) {
  232. list = list.concat(findTopNavs(container));
  233. });
  234. return list;
  235. };
  236. /**
  237. * @hidden
  238. */
  239. App.prototype.present = function (enteringView, opts, appPortal) {
  240. (void 0) /* assert */;
  241. var portal = this._appRoot._getPortal(appPortal);
  242. // Set Nav must be set here in order to dimiss() work synchnously.
  243. // TODO: move _setNav() to the earlier stages of NavController. _queueTrns()
  244. enteringView._setNav(portal);
  245. opts.direction = DIRECTION_FORWARD;
  246. if (!opts.animation) {
  247. opts.animation = enteringView.getTransitionName(DIRECTION_FORWARD);
  248. }
  249. enteringView.setLeavingOpts({
  250. keyboardClose: opts.keyboardClose,
  251. direction: DIRECTION_BACK,
  252. animation: enteringView.getTransitionName(DIRECTION_BACK),
  253. ev: opts.ev
  254. });
  255. return portal.insertPages(-1, [enteringView], opts);
  256. };
  257. /**
  258. * @hidden
  259. */
  260. App.prototype.goBack = function () {
  261. if (this._menuCtrl && this._menuCtrl.isOpen()) {
  262. return this._menuCtrl.close();
  263. }
  264. var navPromise = this.navPop();
  265. if (!navPromise) {
  266. // no views to go back to
  267. // let's exit the app
  268. if (this._config.getBoolean('navExitApp', true)) {
  269. (void 0) /* console.debug */;
  270. this._plt.exitApp();
  271. }
  272. }
  273. return navPromise;
  274. };
  275. /**
  276. * @hidden
  277. */
  278. App.prototype.navPop = function () {
  279. var _this = this;
  280. if (!this._rootNavs || this._rootNavs.size === 0 || !this.isEnabled()) {
  281. return Promise.resolve();
  282. }
  283. // If there are any alert/actionsheet open, let's do nothing
  284. var portal = this._appRoot._getPortal(Constants.PORTAL_DEFAULT);
  285. if (portal.length() > 0) {
  286. return Promise.resolve();
  287. }
  288. var navToPop = null;
  289. var mostRecentVC = null;
  290. this._rootNavs.forEach(function (navContainer) {
  291. var activeNavs = _this.getActiveNavs(navContainer.id);
  292. var poppableNavs = activeNavs.map(function (activeNav) { return getPoppableNav(activeNav); }).filter(function (nav) { return !!nav; });
  293. poppableNavs.forEach(function (poppable) {
  294. var topViewController = poppable.last();
  295. if (poppable._isPortal || (topViewController && poppable.length() > 1 && (!mostRecentVC || topViewController._ts >= mostRecentVC._ts))) {
  296. mostRecentVC = topViewController;
  297. navToPop = poppable;
  298. }
  299. });
  300. });
  301. if (navToPop) {
  302. return navToPop.pop();
  303. }
  304. };
  305. /**
  306. * @hidden
  307. */
  308. App.prototype._enableInputBlurring = function () {
  309. (void 0) /* console.debug */;
  310. var focused = true;
  311. var self = this;
  312. var platform = this._plt;
  313. platform.registerListener(platform.doc(), 'focusin', onFocusin, { capture: true, zone: false, passive: true });
  314. platform.registerListener(platform.doc(), 'touchend', onTouchend, { capture: false, zone: false, passive: true });
  315. function onFocusin() {
  316. focused = true;
  317. }
  318. function onTouchend(ev) {
  319. // if app did scroll return early
  320. if (self._didScroll) {
  321. self._didScroll = false;
  322. return;
  323. }
  324. var active = self._plt.getActiveElement();
  325. if (!active) {
  326. return;
  327. }
  328. // only blur if the active element is a text-input or a textarea
  329. if (SKIP_BLURRING.indexOf(active.tagName) === -1) {
  330. return;
  331. }
  332. // if the selected target is the active element, do not blur
  333. var tapped = ev.target;
  334. if (tapped === active) {
  335. return;
  336. }
  337. if (SKIP_BLURRING.indexOf(tapped.tagName) >= 0) {
  338. return;
  339. }
  340. // skip if div is a cover
  341. if (tapped.classList.contains('input-cover')) {
  342. return;
  343. }
  344. focused = false;
  345. // TODO: find a better way, why 50ms?
  346. platform.timeout(function () {
  347. if (!focused) {
  348. active.blur();
  349. }
  350. }, 50);
  351. }
  352. };
  353. App.prototype.getNavByIdOrName = function (id) {
  354. var navs = Array.from(this._rootNavs.values());
  355. for (var _i = 0, navs_1 = navs; _i < navs_1.length; _i++) {
  356. var navContainer = navs_1[_i];
  357. var match = getNavByIdOrName(navContainer, id);
  358. if (match) {
  359. return match;
  360. }
  361. }
  362. return null;
  363. };
  364. App.decorators = [
  365. { type: Injectable },
  366. ];
  367. /** @nocollapse */
  368. App.ctorParameters = function () { return [
  369. { type: Config, },
  370. { type: Platform, },
  371. { type: MenuController, decorators: [{ type: Optional },] },
  372. ]; };
  373. return App;
  374. }());
  375. export { App };
  376. export function getNavByIdOrName(nav, id) {
  377. if (nav.id === id || nav.name === id) {
  378. return nav;
  379. }
  380. for (var _i = 0, _a = nav.getAllChildNavs(); _i < _a.length; _i++) {
  381. var child = _a[_i];
  382. var tmp = getNavByIdOrName(child, id);
  383. if (tmp) {
  384. return tmp;
  385. }
  386. }
  387. return null;
  388. }
  389. function getPoppableNav(nav) {
  390. if (!nav) {
  391. return null;
  392. }
  393. if (isTabs(nav)) {
  394. // tabs aren't a nav, so just call this function again immediately on the parent on tabs
  395. return getPoppableNav(nav.parent);
  396. }
  397. var len = nav.length();
  398. if (len > 1 || (nav._isPortal && len > 0)) {
  399. // this nav controller has more than one view
  400. // use this nav!
  401. return nav;
  402. }
  403. // try again using the parent nav (if there is one)
  404. return getPoppableNav(nav.parent);
  405. }
  406. export function findTopNavs(nav) {
  407. var containers = [];
  408. var childNavs = nav.getActiveChildNavs();
  409. if (!childNavs || !childNavs.length) {
  410. containers.push(nav);
  411. }
  412. else {
  413. childNavs.forEach(function (childNav) {
  414. var topNavs = findTopNavs(childNav);
  415. containers = containers.concat(topNavs);
  416. });
  417. }
  418. return containers;
  419. }
  420. var SKIP_BLURRING = ['INPUT', 'TEXTAREA', 'ION-INPUT', 'ION-TEXTAREA'];
  421. var ACTIVE_SCROLLING_TIME = 100;
  422. var CLICK_BLOCK_BUFFER_IN_MILLIS = 64;
  423. //# sourceMappingURL=app.js.map