From 210e8feae2fb4842bfb2de38666e6c41671fef3c Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Wed, 27 Sep 2017 12:42:48 -0400 Subject: removed lib --- www/lib/ionic/js/ionic-angular.js | 14044 ------- www/lib/ionic/js/ionic-angular.min.js | 18 - www/lib/ionic/js/ionic.bundle.js | 64386 -------------------------------- www/lib/ionic/js/ionic.bundle.min.js | 448 - www/lib/ionic/js/ionic.js | 13298 ------- www/lib/ionic/js/ionic.min.js | 20 - 6 files changed, 92214 deletions(-) delete mode 100644 www/lib/ionic/js/ionic-angular.js delete mode 100644 www/lib/ionic/js/ionic-angular.min.js delete mode 100644 www/lib/ionic/js/ionic.bundle.js delete mode 100644 www/lib/ionic/js/ionic.bundle.min.js delete mode 100644 www/lib/ionic/js/ionic.js delete mode 100644 www/lib/ionic/js/ionic.min.js (limited to 'www/lib/ionic/js') diff --git a/www/lib/ionic/js/ionic-angular.js b/www/lib/ionic/js/ionic-angular.js deleted file mode 100644 index f3afedec..00000000 --- a/www/lib/ionic/js/ionic-angular.js +++ /dev/null @@ -1,14044 +0,0 @@ -/*! - * Copyright 2015 Drifty Co. - * http://drifty.com/ - * - * Ionic, v1.2.4-nightly-1917 - * A powerful HTML5 mobile app framework. - * http://ionicframework.com/ - * - * By @maxlynch, @benjsperry, @adamdbradley <3 - * - * Licensed under the MIT license. Please see LICENSE for more information. - * - */ - -(function() { -/* eslint no-unused-vars:0 */ -var IonicModule = angular.module('ionic', ['ngAnimate', 'ngSanitize', 'ui.router', 'ngIOS9UIWebViewPatch']), - extend = angular.extend, - forEach = angular.forEach, - isDefined = angular.isDefined, - isNumber = angular.isNumber, - isString = angular.isString, - jqLite = angular.element, - noop = angular.noop; - -/** - * @ngdoc service - * @name $ionicActionSheet - * @module ionic - * @description - * The Action Sheet is a slide-up pane that lets the user choose from a set of options. - * Dangerous options are highlighted in red and made obvious. - * - * There are easy ways to cancel out of the action sheet, such as tapping the backdrop or even - * hitting escape on the keyboard for desktop testing. - * - * ![Action Sheet](http://ionicframework.com.s3.amazonaws.com/docs/controllers/actionSheet.gif) - * - * @usage - * To trigger an Action Sheet in your code, use the $ionicActionSheet service in your angular controllers: - * - * ```js - * angular.module('mySuperApp', ['ionic']) - * .controller(function($scope, $ionicActionSheet, $timeout) { - * - * // Triggered on a button click, or some other target - * $scope.show = function() { - * - * // Show the action sheet - * var hideSheet = $ionicActionSheet.show({ - * buttons: [ - * { text: 'Share This' }, - * { text: 'Move' } - * ], - * destructiveText: 'Delete', - * titleText: 'Modify your album', - * cancelText: 'Cancel', - * cancel: function() { - // add cancel code.. - }, - * buttonClicked: function(index) { - * return true; - * } - * }); - * - * // For example's sake, hide the sheet after two seconds - * $timeout(function() { - * hideSheet(); - * }, 2000); - * - * }; - * }); - * ``` - * - */ -IonicModule -.factory('$ionicActionSheet', [ - '$rootScope', - '$compile', - '$animate', - '$timeout', - '$ionicTemplateLoader', - '$ionicPlatform', - '$ionicBody', - 'IONIC_BACK_PRIORITY', -function($rootScope, $compile, $animate, $timeout, $ionicTemplateLoader, $ionicPlatform, $ionicBody, IONIC_BACK_PRIORITY) { - - return { - show: actionSheet - }; - - /** - * @ngdoc method - * @name $ionicActionSheet#show - * @description - * Load and return a new action sheet. - * - * A new isolated scope will be created for the - * action sheet and the new element will be appended into the body. - * - * @param {object} options The options for this ActionSheet. Properties: - * - * - `[Object]` `buttons` Which buttons to show. Each button is an object with a `text` field. - * - `{string}` `titleText` The title to show on the action sheet. - * - `{string=}` `cancelText` the text for a 'cancel' button on the action sheet. - * - `{string=}` `destructiveText` The text for a 'danger' on the action sheet. - * - `{function=}` `cancel` Called if the cancel button is pressed, the backdrop is tapped or - * the hardware back button is pressed. - * - `{function=}` `buttonClicked` Called when one of the non-destructive buttons is clicked, - * with the index of the button that was clicked and the button object. Return true to close - * the action sheet, or false to keep it opened. - * - `{function=}` `destructiveButtonClicked` Called when the destructive button is clicked. - * Return true to close the action sheet, or false to keep it opened. - * - `{boolean=}` `cancelOnStateChange` Whether to cancel the actionSheet when navigating - * to a new state. Default true. - * - `{string}` `cssClass` The custom CSS class name. - * - * @returns {function} `hideSheet` A function which, when called, hides & cancels the action sheet. - */ - function actionSheet(opts) { - var scope = $rootScope.$new(true); - - extend(scope, { - cancel: noop, - destructiveButtonClicked: noop, - buttonClicked: noop, - $deregisterBackButton: noop, - buttons: [], - cancelOnStateChange: true - }, opts || {}); - - function textForIcon(text) { - if (text && /icon/.test(text)) { - scope.$actionSheetHasIcon = true; - } - } - - for (var x = 0; x < scope.buttons.length; x++) { - textForIcon(scope.buttons[x].text); - } - textForIcon(scope.cancelText); - textForIcon(scope.destructiveText); - - // Compile the template - var element = scope.element = $compile('')(scope); - - // Grab the sheet element for animation - var sheetEl = jqLite(element[0].querySelector('.action-sheet-wrapper')); - - var stateChangeListenDone = scope.cancelOnStateChange ? - $rootScope.$on('$stateChangeSuccess', function() { scope.cancel(); }) : - noop; - - // removes the actionSheet from the screen - scope.removeSheet = function(done) { - if (scope.removed) return; - - scope.removed = true; - sheetEl.removeClass('action-sheet-up'); - $timeout(function() { - // wait to remove this due to a 300ms delay native - // click which would trigging whatever was underneath this - $ionicBody.removeClass('action-sheet-open'); - }, 400); - scope.$deregisterBackButton(); - stateChangeListenDone(); - - $animate.removeClass(element, 'active').then(function() { - scope.$destroy(); - element.remove(); - // scope.cancel.$scope is defined near the bottom - scope.cancel.$scope = sheetEl = null; - (done || noop)(opts.buttons); - }); - }; - - scope.showSheet = function(done) { - if (scope.removed) return; - - $ionicBody.append(element) - .addClass('action-sheet-open'); - - $animate.addClass(element, 'active').then(function() { - if (scope.removed) return; - (done || noop)(); - }); - $timeout(function() { - if (scope.removed) return; - sheetEl.addClass('action-sheet-up'); - }, 20, false); - }; - - // registerBackButtonAction returns a callback to deregister the action - scope.$deregisterBackButton = $ionicPlatform.registerBackButtonAction( - function() { - $timeout(scope.cancel); - }, - IONIC_BACK_PRIORITY.actionSheet - ); - - // called when the user presses the cancel button - scope.cancel = function() { - // after the animation is out, call the cancel callback - scope.removeSheet(opts.cancel); - }; - - scope.buttonClicked = function(index) { - // Check if the button click event returned true, which means - // we can close the action sheet - if (opts.buttonClicked(index, opts.buttons[index]) === true) { - scope.removeSheet(); - } - }; - - scope.destructiveButtonClicked = function() { - // Check if the destructive button click event returned true, which means - // we can close the action sheet - if (opts.destructiveButtonClicked() === true) { - scope.removeSheet(); - } - }; - - scope.showSheet(); - - // Expose the scope on $ionicActionSheet's return value for the sake - // of testing it. - scope.cancel.$scope = scope; - - return scope.cancel; - } -}]); - - -jqLite.prototype.addClass = function(cssClasses) { - var x, y, cssClass, el, splitClasses, existingClasses; - if (cssClasses && cssClasses != 'ng-scope' && cssClasses != 'ng-isolate-scope') { - for (x = 0; x < this.length; x++) { - el = this[x]; - if (el.setAttribute) { - - if (cssClasses.indexOf(' ') < 0 && el.classList.add) { - el.classList.add(cssClasses); - } else { - existingClasses = (' ' + (el.getAttribute('class') || '') + ' ') - .replace(/[\n\t]/g, " "); - splitClasses = cssClasses.split(' '); - - for (y = 0; y < splitClasses.length; y++) { - cssClass = splitClasses[y].trim(); - if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { - existingClasses += cssClass + ' '; - } - } - el.setAttribute('class', existingClasses.trim()); - } - } - } - } - return this; -}; - -jqLite.prototype.removeClass = function(cssClasses) { - var x, y, splitClasses, cssClass, el; - if (cssClasses) { - for (x = 0; x < this.length; x++) { - el = this[x]; - if (el.getAttribute) { - if (cssClasses.indexOf(' ') < 0 && el.classList.remove) { - el.classList.remove(cssClasses); - } else { - splitClasses = cssClasses.split(' '); - - for (y = 0; y < splitClasses.length; y++) { - cssClass = splitClasses[y]; - el.setAttribute('class', ( - (" " + (el.getAttribute('class') || '') + " ") - .replace(/[\n\t]/g, " ") - .replace(" " + cssClass.trim() + " ", " ")).trim() - ); - } - } - } - } - } - return this; -}; - -/** - * @ngdoc service - * @name $ionicBackdrop - * @module ionic - * @description - * Shows and hides a backdrop over the UI. Appears behind popups, loading, - * and other overlays. - * - * Often, multiple UI components require a backdrop, but only one backdrop is - * ever needed in the DOM at a time. - * - * Therefore, each component that requires the backdrop to be shown calls - * `$ionicBackdrop.retain()` when it wants the backdrop, then `$ionicBackdrop.release()` - * when it is done with the backdrop. - * - * For each time `retain` is called, the backdrop will be shown until `release` is called. - * - * For example, if `retain` is called three times, the backdrop will be shown until `release` - * is called three times. - * - * **Notes:** - * - The backdrop service will broadcast 'backdrop.shown' and 'backdrop.hidden' events from the root scope, - * this is useful for alerting native components not in html. - * - * @usage - * - * ```js - * function MyController($scope, $ionicBackdrop, $timeout, $rootScope) { - * //Show a backdrop for one second - * $scope.action = function() { - * $ionicBackdrop.retain(); - * $timeout(function() { - * $ionicBackdrop.release(); - * }, 1000); - * }; - * - * // Execute action on backdrop disappearing - * $scope.$on('backdrop.hidden', function() { - * // Execute action - * }); - * - * // Execute action on backdrop appearing - * $scope.$on('backdrop.shown', function() { - * // Execute action - * }); - * - * } - * ``` - */ -IonicModule -.factory('$ionicBackdrop', [ - '$document', '$timeout', '$$rAF', '$rootScope', -function($document, $timeout, $$rAF, $rootScope) { - - var el = jqLite('
'); - var backdropHolds = 0; - - $document[0].body.appendChild(el[0]); - - return { - /** - * @ngdoc method - * @name $ionicBackdrop#retain - * @description Retains the backdrop. - */ - retain: retain, - /** - * @ngdoc method - * @name $ionicBackdrop#release - * @description - * Releases the backdrop. - */ - release: release, - - getElement: getElement, - - // exposed for testing - _element: el - }; - - function retain() { - backdropHolds++; - if (backdropHolds === 1) { - el.addClass('visible'); - $rootScope.$broadcast('backdrop.shown'); - $$rAF(function() { - // If we're still at >0 backdropHolds after async... - if (backdropHolds >= 1) el.addClass('active'); - }); - } - } - function release() { - if (backdropHolds === 1) { - el.removeClass('active'); - $rootScope.$broadcast('backdrop.hidden'); - $timeout(function() { - // If we're still at 0 backdropHolds after async... - if (backdropHolds === 0) el.removeClass('visible'); - }, 400, false); - } - backdropHolds = Math.max(0, backdropHolds - 1); - } - - function getElement() { - return el; - } - -}]); - -/** - * @private - */ -IonicModule -.factory('$ionicBind', ['$parse', '$interpolate', function($parse, $interpolate) { - var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; - return function(scope, attrs, bindDefinition) { - forEach(bindDefinition || {}, function(definition, scopeName) { - //Adapted from angular.js $compile - var match = definition.match(LOCAL_REGEXP) || [], - attrName = match[3] || scopeName, - mode = match[1], // @, =, or & - parentGet, - unwatch; - - switch (mode) { - case '@': - if (!attrs[attrName]) { - return; - } - attrs.$observe(attrName, function(value) { - scope[scopeName] = value; - }); - // we trigger an interpolation to ensure - // the value is there for use immediately - if (attrs[attrName]) { - scope[scopeName] = $interpolate(attrs[attrName])(scope); - } - break; - - case '=': - if (!attrs[attrName]) { - return; - } - unwatch = scope.$watch(attrs[attrName], function(value) { - scope[scopeName] = value; - }); - //Destroy parent scope watcher when this scope is destroyed - scope.$on('$destroy', unwatch); - break; - - case '&': - /* jshint -W044 */ - if (attrs[attrName] && attrs[attrName].match(RegExp(scopeName + '\(.*?\)'))) { - throw new Error('& expression binding "' + scopeName + '" looks like it will recursively call "' + - attrs[attrName] + '" and cause a stack overflow! Please choose a different scopeName.'); - } - parentGet = $parse(attrs[attrName]); - scope[scopeName] = function(locals) { - return parentGet(scope, locals); - }; - break; - } - }); - }; -}]); - -/** - * @ngdoc service - * @name $ionicBody - * @module ionic - * @description An angular utility service to easily and efficiently - * add and remove CSS classes from the document's body element. - */ -IonicModule -.factory('$ionicBody', ['$document', function($document) { - return { - /** - * @ngdoc method - * @name $ionicBody#addClass - * @description Add a class to the document's body element. - * @param {string} class Each argument will be added to the body element. - * @returns {$ionicBody} The $ionicBody service so methods can be chained. - */ - addClass: function() { - for (var x = 0; x < arguments.length; x++) { - $document[0].body.classList.add(arguments[x]); - } - return this; - }, - /** - * @ngdoc method - * @name $ionicBody#removeClass - * @description Remove a class from the document's body element. - * @param {string} class Each argument will be removed from the body element. - * @returns {$ionicBody} The $ionicBody service so methods can be chained. - */ - removeClass: function() { - for (var x = 0; x < arguments.length; x++) { - $document[0].body.classList.remove(arguments[x]); - } - return this; - }, - /** - * @ngdoc method - * @name $ionicBody#enableClass - * @description Similar to the `add` method, except the first parameter accepts a boolean - * value determining if the class should be added or removed. Rather than writing user code, - * such as "if true then add the class, else then remove the class", this method can be - * given a true or false value which reduces redundant code. - * @param {boolean} shouldEnableClass A true/false value if the class should be added or removed. - * @param {string} class Each remaining argument would be added or removed depending on - * the first argument. - * @returns {$ionicBody} The $ionicBody service so methods can be chained. - */ - enableClass: function(shouldEnableClass) { - var args = Array.prototype.slice.call(arguments).slice(1); - if (shouldEnableClass) { - this.addClass.apply(this, args); - } else { - this.removeClass.apply(this, args); - } - return this; - }, - /** - * @ngdoc method - * @name $ionicBody#append - * @description Append a child to the document's body. - * @param {element} element The element to be appended to the body. The passed in element - * can be either a jqLite element, or a DOM element. - * @returns {$ionicBody} The $ionicBody service so methods can be chained. - */ - append: function(ele) { - $document[0].body.appendChild(ele.length ? ele[0] : ele); - return this; - }, - /** - * @ngdoc method - * @name $ionicBody#get - * @description Get the document's body element. - * @returns {element} Returns the document's body element. - */ - get: function() { - return $document[0].body; - } - }; -}]); - -IonicModule -.factory('$ionicClickBlock', [ - '$document', - '$ionicBody', - '$timeout', -function($document, $ionicBody, $timeout) { - var CSS_HIDE = 'click-block-hide'; - var cbEle, fallbackTimer, pendingShow; - - function preventClick(ev) { - ev.preventDefault(); - ev.stopPropagation(); - } - - function addClickBlock() { - if (pendingShow) { - if (cbEle) { - cbEle.classList.remove(CSS_HIDE); - } else { - cbEle = $document[0].createElement('div'); - cbEle.className = 'click-block'; - $ionicBody.append(cbEle); - cbEle.addEventListener('touchstart', preventClick); - cbEle.addEventListener('mousedown', preventClick); - } - pendingShow = false; - } - } - - function removeClickBlock() { - cbEle && cbEle.classList.add(CSS_HIDE); - } - - return { - show: function(autoExpire) { - pendingShow = true; - $timeout.cancel(fallbackTimer); - fallbackTimer = $timeout(this.hide, autoExpire || 310, false); - addClickBlock(); - }, - hide: function() { - pendingShow = false; - $timeout.cancel(fallbackTimer); - removeClickBlock(); - } - }; -}]); - -/** - * @ngdoc service - * @name $ionicGesture - * @module ionic - * @description An angular service exposing ionic - * {@link ionic.utility:ionic.EventController}'s gestures. - */ -IonicModule -.factory('$ionicGesture', [function() { - return { - /** - * @ngdoc method - * @name $ionicGesture#on - * @description Add an event listener for a gesture on an element. See {@link ionic.utility:ionic.EventController#onGesture}. - * @param {string} eventType The gesture event to listen for. - * @param {function(e)} callback The function to call when the gesture - * happens. - * @param {element} $element The angular element to listen for the event on. - * @param {object} options object. - * @returns {ionic.Gesture} The gesture object (use this to remove the gesture later on). - */ - on: function(eventType, cb, $element, options) { - return window.ionic.onGesture(eventType, cb, $element[0], options); - }, - /** - * @ngdoc method - * @name $ionicGesture#off - * @description Remove an event listener for a gesture on an element. See {@link ionic.utility:ionic.EventController#offGesture}. - * @param {ionic.Gesture} gesture The gesture that should be removed. - * @param {string} eventType The gesture event to remove the listener for. - * @param {function(e)} callback The listener to remove. - */ - off: function(gesture, eventType, cb) { - return window.ionic.offGesture(gesture, eventType, cb); - } - }; -}]); - -/** - * @ngdoc service - * @name $ionicHistory - * @module ionic - * @description - * $ionicHistory keeps track of views as the user navigates through an app. Similar to the way a - * browser behaves, an Ionic app is able to keep track of the previous view, the current view, and - * the forward view (if there is one). However, a typical web browser only keeps track of one - * history stack in a linear fashion. - * - * Unlike a traditional browser environment, apps and webapps have parallel independent histories, - * such as with tabs. Should a user navigate few pages deep on one tab, and then switch to a new - * tab and back, the back button relates not to the previous tab, but to the previous pages - * visited within _that_ tab. - * - * `$ionicHistory` facilitates this parallel history architecture. - */ - -IonicModule -.factory('$ionicHistory', [ - '$rootScope', - '$state', - '$location', - '$window', - '$timeout', - '$ionicViewSwitcher', - '$ionicNavViewDelegate', -function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $ionicNavViewDelegate) { - - // history actions while navigating views - var ACTION_INITIAL_VIEW = 'initialView'; - var ACTION_NEW_VIEW = 'newView'; - var ACTION_MOVE_BACK = 'moveBack'; - var ACTION_MOVE_FORWARD = 'moveForward'; - - // direction of navigation - var DIRECTION_BACK = 'back'; - var DIRECTION_FORWARD = 'forward'; - var DIRECTION_ENTER = 'enter'; - var DIRECTION_EXIT = 'exit'; - var DIRECTION_SWAP = 'swap'; - var DIRECTION_NONE = 'none'; - - var stateChangeCounter = 0; - var lastStateId, nextViewOptions, deregisterStateChangeListener, nextViewExpireTimer, forcedNav; - - var viewHistory = { - histories: { root: { historyId: 'root', parentHistoryId: null, stack: [], cursor: -1 } }, - views: {}, - backView: null, - forwardView: null, - currentView: null - }; - - var View = function() {}; - View.prototype.initialize = function(data) { - if (data) { - for (var name in data) this[name] = data[name]; - return this; - } - return null; - }; - View.prototype.go = function() { - - if (this.stateName) { - return $state.go(this.stateName, this.stateParams); - } - - if (this.url && this.url !== $location.url()) { - - if (viewHistory.backView === this) { - return $window.history.go(-1); - } else if (viewHistory.forwardView === this) { - return $window.history.go(1); - } - - $location.url(this.url); - } - - return null; - }; - View.prototype.destroy = function() { - if (this.scope) { - this.scope.$destroy && this.scope.$destroy(); - this.scope = null; - } - }; - - - function getViewById(viewId) { - return (viewId ? viewHistory.views[ viewId ] : null); - } - - function getBackView(view) { - return (view ? getViewById(view.backViewId) : null); - } - - function getForwardView(view) { - return (view ? getViewById(view.forwardViewId) : null); - } - - function getHistoryById(historyId) { - return (historyId ? viewHistory.histories[ historyId ] : null); - } - - function getHistory(scope) { - var histObj = getParentHistoryObj(scope); - - if (!viewHistory.histories[ histObj.historyId ]) { - // this history object exists in parent scope, but doesn't - // exist in the history data yet - viewHistory.histories[ histObj.historyId ] = { - historyId: histObj.historyId, - parentHistoryId: getParentHistoryObj(histObj.scope.$parent).historyId, - stack: [], - cursor: -1 - }; - } - return getHistoryById(histObj.historyId); - } - - function getParentHistoryObj(scope) { - var parentScope = scope; - while (parentScope) { - if (parentScope.hasOwnProperty('$historyId')) { - // this parent scope has a historyId - return { historyId: parentScope.$historyId, scope: parentScope }; - } - // nothing found keep climbing up - parentScope = parentScope.$parent; - } - // no history for the parent, use the root - return { historyId: 'root', scope: $rootScope }; - } - - function setNavViews(viewId) { - viewHistory.currentView = getViewById(viewId); - viewHistory.backView = getBackView(viewHistory.currentView); - viewHistory.forwardView = getForwardView(viewHistory.currentView); - } - - function getCurrentStateId() { - var id; - if ($state && $state.current && $state.current.name) { - id = $state.current.name; - if ($state.params) { - for (var key in $state.params) { - if ($state.params.hasOwnProperty(key) && $state.params[key]) { - id += "_" + key + "=" + $state.params[key]; - } - } - } - return id; - } - // if something goes wrong make sure its got a unique stateId - return ionic.Utils.nextUid(); - } - - function getCurrentStateParams() { - var rtn; - if ($state && $state.params) { - for (var key in $state.params) { - if ($state.params.hasOwnProperty(key)) { - rtn = rtn || {}; - rtn[key] = $state.params[key]; - } - } - } - return rtn; - } - - - return { - - register: function(parentScope, viewLocals) { - - var currentStateId = getCurrentStateId(), - hist = getHistory(parentScope), - currentView = viewHistory.currentView, - backView = viewHistory.backView, - forwardView = viewHistory.forwardView, - viewId = null, - action = null, - direction = DIRECTION_NONE, - historyId = hist.historyId, - url = $location.url(), - tmp, x, ele; - - if (lastStateId !== currentStateId) { - lastStateId = currentStateId; - stateChangeCounter++; - } - - if (forcedNav) { - // we've previously set exactly what to do - viewId = forcedNav.viewId; - action = forcedNav.action; - direction = forcedNav.direction; - forcedNav = null; - - } else if (backView && backView.stateId === currentStateId) { - // they went back one, set the old current view as a forward view - viewId = backView.viewId; - historyId = backView.historyId; - action = ACTION_MOVE_BACK; - if (backView.historyId === currentView.historyId) { - // went back in the same history - direction = DIRECTION_BACK; - - } else if (currentView) { - direction = DIRECTION_EXIT; - - tmp = getHistoryById(backView.historyId); - if (tmp && tmp.parentHistoryId === currentView.historyId) { - direction = DIRECTION_ENTER; - - } else { - tmp = getHistoryById(currentView.historyId); - if (tmp && tmp.parentHistoryId === hist.parentHistoryId) { - direction = DIRECTION_SWAP; - } - } - } - - } else if (forwardView && forwardView.stateId === currentStateId) { - // they went to the forward one, set the forward view to no longer a forward view - viewId = forwardView.viewId; - historyId = forwardView.historyId; - action = ACTION_MOVE_FORWARD; - if (forwardView.historyId === currentView.historyId) { - direction = DIRECTION_FORWARD; - - } else if (currentView) { - direction = DIRECTION_EXIT; - - if (currentView.historyId === hist.parentHistoryId) { - direction = DIRECTION_ENTER; - - } else { - tmp = getHistoryById(currentView.historyId); - if (tmp && tmp.parentHistoryId === hist.parentHistoryId) { - direction = DIRECTION_SWAP; - } - } - } - - tmp = getParentHistoryObj(parentScope); - if (forwardView.historyId && tmp.scope) { - // if a history has already been created by the forward view then make sure it stays the same - tmp.scope.$historyId = forwardView.historyId; - historyId = forwardView.historyId; - } - - } else if (currentView && currentView.historyId !== historyId && - hist.cursor > -1 && hist.stack.length > 0 && hist.cursor < hist.stack.length && - hist.stack[hist.cursor].stateId === currentStateId) { - // they just changed to a different history and the history already has views in it - var switchToView = hist.stack[hist.cursor]; - viewId = switchToView.viewId; - historyId = switchToView.historyId; - action = ACTION_MOVE_BACK; - direction = DIRECTION_SWAP; - - tmp = getHistoryById(currentView.historyId); - if (tmp && tmp.parentHistoryId === historyId) { - direction = DIRECTION_EXIT; - - } else { - tmp = getHistoryById(historyId); - if (tmp && tmp.parentHistoryId === currentView.historyId) { - direction = DIRECTION_ENTER; - } - } - - // if switching to a different history, and the history of the view we're switching - // to has an existing back view from a different history than itself, then - // it's back view would be better represented using the current view as its back view - tmp = getViewById(switchToView.backViewId); - if (tmp && switchToView.historyId !== tmp.historyId) { - hist.stack[hist.cursor].backViewId = currentView.viewId; - } - - } else { - - // create an element from the viewLocals template - ele = $ionicViewSwitcher.createViewEle(viewLocals); - if (this.isAbstractEle(ele, viewLocals)) { - void 0; - return { - action: 'abstractView', - direction: DIRECTION_NONE, - ele: ele - }; - } - - // set a new unique viewId - viewId = ionic.Utils.nextUid(); - - if (currentView) { - // set the forward view if there is a current view (ie: if its not the first view) - currentView.forwardViewId = viewId; - - action = ACTION_NEW_VIEW; - - // check if there is a new forward view within the same history - if (forwardView && currentView.stateId !== forwardView.stateId && - currentView.historyId === forwardView.historyId) { - // they navigated to a new view but the stack already has a forward view - // since its a new view remove any forwards that existed - tmp = getHistoryById(forwardView.historyId); - if (tmp) { - // the forward has a history - for (x = tmp.stack.length - 1; x >= forwardView.index; x--) { - // starting from the end destroy all forwards in this history from this point - var stackItem = tmp.stack[x]; - stackItem && stackItem.destroy && stackItem.destroy(); - tmp.stack.splice(x); - } - historyId = forwardView.historyId; - } - } - - // its only moving forward if its in the same history - if (hist.historyId === currentView.historyId) { - direction = DIRECTION_FORWARD; - - } else if (currentView.historyId !== hist.historyId) { - direction = DIRECTION_ENTER; - - tmp = getHistoryById(currentView.historyId); - if (tmp && tmp.parentHistoryId === hist.parentHistoryId) { - direction = DIRECTION_SWAP; - - } else { - tmp = getHistoryById(tmp.parentHistoryId); - if (tmp && tmp.historyId === hist.historyId) { - direction = DIRECTION_EXIT; - } - } - } - - } else { - // there's no current view, so this must be the initial view - action = ACTION_INITIAL_VIEW; - } - - if (stateChangeCounter < 2) { - // views that were spun up on the first load should not animate - direction = DIRECTION_NONE; - } - - // add the new view - viewHistory.views[viewId] = this.createView({ - viewId: viewId, - index: hist.stack.length, - historyId: hist.historyId, - backViewId: (currentView && currentView.viewId ? currentView.viewId : null), - forwardViewId: null, - stateId: currentStateId, - stateName: this.currentStateName(), - stateParams: getCurrentStateParams(), - url: url, - canSwipeBack: canSwipeBack(ele, viewLocals) - }); - - // add the new view to this history's stack - hist.stack.push(viewHistory.views[viewId]); - } - - deregisterStateChangeListener && deregisterStateChangeListener(); - $timeout.cancel(nextViewExpireTimer); - if (nextViewOptions) { - if (nextViewOptions.disableAnimate) direction = DIRECTION_NONE; - if (nextViewOptions.disableBack) viewHistory.views[viewId].backViewId = null; - if (nextViewOptions.historyRoot) { - for (x = 0; x < hist.stack.length; x++) { - if (hist.stack[x].viewId === viewId) { - hist.stack[x].index = 0; - hist.stack[x].backViewId = hist.stack[x].forwardViewId = null; - } else { - delete viewHistory.views[hist.stack[x].viewId]; - } - } - hist.stack = [viewHistory.views[viewId]]; - } - nextViewOptions = null; - } - - setNavViews(viewId); - - if (viewHistory.backView && historyId == viewHistory.backView.historyId && currentStateId == viewHistory.backView.stateId && url == viewHistory.backView.url) { - for (x = 0; x < hist.stack.length; x++) { - if (hist.stack[x].viewId == viewId) { - action = 'dupNav'; - direction = DIRECTION_NONE; - if (x > 0) { - hist.stack[x - 1].forwardViewId = null; - } - viewHistory.forwardView = null; - viewHistory.currentView.index = viewHistory.backView.index; - viewHistory.currentView.backViewId = viewHistory.backView.backViewId; - viewHistory.backView = getBackView(viewHistory.backView); - hist.stack.splice(x, 1); - break; - } - } - } - - void 0; - - hist.cursor = viewHistory.currentView.index; - - return { - viewId: viewId, - action: action, - direction: direction, - historyId: historyId, - enableBack: this.enabledBack(viewHistory.currentView), - isHistoryRoot: (viewHistory.currentView.index === 0), - ele: ele - }; - }, - - registerHistory: function(scope) { - scope.$historyId = ionic.Utils.nextUid(); - }, - - createView: function(data) { - var newView = new View(); - return newView.initialize(data); - }, - - getViewById: getViewById, - - /** - * @ngdoc method - * @name $ionicHistory#viewHistory - * @description The app's view history data, such as all the views and histories, along - * with how they are ordered and linked together within the navigation stack. - * @returns {object} Returns an object containing the apps view history data. - */ - viewHistory: function() { - return viewHistory; - }, - - /** - * @ngdoc method - * @name $ionicHistory#currentView - * @description The app's current view. - * @returns {object} Returns the current view. - */ - currentView: function(view) { - if (arguments.length) { - viewHistory.currentView = view; - } - return viewHistory.currentView; - }, - - /** - * @ngdoc method - * @name $ionicHistory#currentHistoryId - * @description The ID of the history stack which is the parent container of the current view. - * @returns {string} Returns the current history ID. - */ - currentHistoryId: function() { - return viewHistory.currentView ? viewHistory.currentView.historyId : null; - }, - - /** - * @ngdoc method - * @name $ionicHistory#currentTitle - * @description Gets and sets the current view's title. - * @param {string=} val The title to update the current view with. - * @returns {string} Returns the current view's title. - */ - currentTitle: function(val) { - if (viewHistory.currentView) { - if (arguments.length) { - viewHistory.currentView.title = val; - } - return viewHistory.currentView.title; - } - }, - - /** - * @ngdoc method - * @name $ionicHistory#backView - * @description Returns the view that was before the current view in the history stack. - * If the user navigated from View A to View B, then View A would be the back view, and - * View B would be the current view. - * @returns {object} Returns the back view. - */ - backView: function(view) { - if (arguments.length) { - viewHistory.backView = view; - } - return viewHistory.backView; - }, - - /** - * @ngdoc method - * @name $ionicHistory#backTitle - * @description Gets the back view's title. - * @returns {string} Returns the back view's title. - */ - backTitle: function(view) { - var backView = (view && getViewById(view.backViewId)) || viewHistory.backView; - return backView && backView.title; - }, - - /** - * @ngdoc method - * @name $ionicHistory#forwardView - * @description Returns the view that was in front of the current view in the history stack. - * A forward view would exist if the user navigated from View A to View B, then - * navigated back to View A. At this point then View B would be the forward view, and View - * A would be the current view. - * @returns {object} Returns the forward view. - */ - forwardView: function(view) { - if (arguments.length) { - viewHistory.forwardView = view; - } - return viewHistory.forwardView; - }, - - /** - * @ngdoc method - * @name $ionicHistory#currentStateName - * @description Returns the current state name. - * @returns {string} - */ - currentStateName: function() { - return ($state && $state.current ? $state.current.name : null); - }, - - isCurrentStateNavView: function(navView) { - return !!($state && $state.current && $state.current.views && $state.current.views[navView]); - }, - - goToHistoryRoot: function(historyId) { - if (historyId) { - var hist = getHistoryById(historyId); - if (hist && hist.stack.length) { - if (viewHistory.currentView && viewHistory.currentView.viewId === hist.stack[0].viewId) { - return; - } - forcedNav = { - viewId: hist.stack[0].viewId, - action: ACTION_MOVE_BACK, - direction: DIRECTION_BACK - }; - hist.stack[0].go(); - } - } - }, - - /** - * @ngdoc method - * @name $ionicHistory#goBack - * @param {number=} backCount Optional negative integer setting how many views to go - * back. By default it'll go back one view by using the value `-1`. To go back two - * views you would use `-2`. If the number goes farther back than the number of views - * in the current history's stack then it'll go to the first view in the current history's - * stack. If the number is zero or greater then it'll do nothing. It also does not - * cross history stacks, meaning it can only go as far back as the current history. - * @description Navigates the app to the back view, if a back view exists. - */ - goBack: function(backCount) { - if (isDefined(backCount) && backCount !== -1) { - if (backCount > -1) return; - - var currentHistory = viewHistory.histories[this.currentHistoryId()]; - var newCursor = currentHistory.cursor + backCount + 1; - if (newCursor < 1) { - newCursor = 1; - } - - currentHistory.cursor = newCursor; - setNavViews(currentHistory.stack[newCursor].viewId); - - var cursor = newCursor - 1; - var clearStateIds = []; - var fwdView = getViewById(currentHistory.stack[cursor].forwardViewId); - while (fwdView) { - clearStateIds.push(fwdView.stateId || fwdView.viewId); - cursor++; - if (cursor >= currentHistory.stack.length) break; - fwdView = getViewById(currentHistory.stack[cursor].forwardViewId); - } - - var self = this; - if (clearStateIds.length) { - $timeout(function() { - self.clearCache(clearStateIds); - }, 600); - } - } - - viewHistory.backView && viewHistory.backView.go(); - }, - - - enabledBack: function(view) { - var backView = getBackView(view); - return !!(backView && backView.historyId === view.historyId); - }, - - /** - * @ngdoc method - * @name $ionicHistory#clearHistory - * @description Clears out the app's entire history, except for the current view. - */ - clearHistory: function() { - var - histories = viewHistory.histories, - currentView = viewHistory.currentView; - - if (histories) { - for (var historyId in histories) { - - if (histories[historyId].stack) { - histories[historyId].stack = []; - histories[historyId].cursor = -1; - } - - if (currentView && currentView.historyId === historyId) { - currentView.backViewId = currentView.forwardViewId = null; - histories[historyId].stack.push(currentView); - } else if (histories[historyId].destroy) { - histories[historyId].destroy(); - } - - } - } - - for (var viewId in viewHistory.views) { - if (viewId !== currentView.viewId) { - delete viewHistory.views[viewId]; - } - } - - if (currentView) { - setNavViews(currentView.viewId); - } - }, - - /** - * @ngdoc method - * @name $ionicHistory#clearCache - * @return promise - * @description Removes all cached views within every {@link ionic.directive:ionNavView}. - * This both removes the view element from the DOM, and destroy it's scope. - */ - clearCache: function(stateIds) { - return $timeout(function() { - $ionicNavViewDelegate._instances.forEach(function(instance) { - instance.clearCache(stateIds); - }); - }); - }, - - /** - * @ngdoc method - * @name $ionicHistory#nextViewOptions - * @description Sets options for the next view. This method can be useful to override - * certain view/transition defaults right before a view transition happens. For example, - * the {@link ionic.directive:menuClose} directive uses this method internally to ensure - * an animated view transition does not happen when a side menu is open, and also sets - * the next view as the root of its history stack. After the transition these options - * are set back to null. - * - * Available options: - * - * * `disableAnimate`: Do not animate the next transition. - * * `disableBack`: The next view should forget its back view, and set it to null. - * * `historyRoot`: The next view should become the root view in its history stack. - * - * ```js - * $ionicHistory.nextViewOptions({ - * disableAnimate: true, - * disableBack: true - * }); - * ``` - */ - nextViewOptions: function(opts) { - deregisterStateChangeListener && deregisterStateChangeListener(); - if (arguments.length) { - $timeout.cancel(nextViewExpireTimer); - if (opts === null) { - nextViewOptions = opts; - } else { - nextViewOptions = nextViewOptions || {}; - extend(nextViewOptions, opts); - if (nextViewOptions.expire) { - deregisterStateChangeListener = $rootScope.$on('$stateChangeSuccess', function() { - nextViewExpireTimer = $timeout(function() { - nextViewOptions = null; - }, nextViewOptions.expire); - }); - } - } - } - return nextViewOptions; - }, - - isAbstractEle: function(ele, viewLocals) { - if (viewLocals && viewLocals.$$state && viewLocals.$$state.self['abstract']) { - return true; - } - return !!(ele && (isAbstractTag(ele) || isAbstractTag(ele.children()))); - }, - - isActiveScope: function(scope) { - if (!scope) return false; - - var climbScope = scope; - var currentHistoryId = this.currentHistoryId(); - var foundHistoryId; - - while (climbScope) { - if (climbScope.$$disconnected) { - return false; - } - - if (!foundHistoryId && climbScope.hasOwnProperty('$historyId')) { - foundHistoryId = true; - } - - if (currentHistoryId) { - if (climbScope.hasOwnProperty('$historyId') && currentHistoryId == climbScope.$historyId) { - return true; - } - if (climbScope.hasOwnProperty('$activeHistoryId')) { - if (currentHistoryId == climbScope.$activeHistoryId) { - if (climbScope.hasOwnProperty('$historyId')) { - return true; - } - if (!foundHistoryId) { - return true; - } - } - } - } - - if (foundHistoryId && climbScope.hasOwnProperty('$activeHistoryId')) { - foundHistoryId = false; - } - - climbScope = climbScope.$parent; - } - - return currentHistoryId ? currentHistoryId == 'root' : true; - } - - }; - - function isAbstractTag(ele) { - return ele && ele.length && /ion-side-menus|ion-tabs/i.test(ele[0].tagName); - } - - function canSwipeBack(ele, viewLocals) { - if (viewLocals && viewLocals.$$state && viewLocals.$$state.self.canSwipeBack === false) { - return false; - } - if (ele && ele.attr('can-swipe-back') === 'false') { - return false; - } - return true; - } - -}]) - -.run([ - '$rootScope', - '$state', - '$location', - '$document', - '$ionicPlatform', - '$ionicHistory', - 'IONIC_BACK_PRIORITY', -function($rootScope, $state, $location, $document, $ionicPlatform, $ionicHistory, IONIC_BACK_PRIORITY) { - - // always reset the keyboard state when change stage - $rootScope.$on('$ionicView.beforeEnter', function() { - ionic.keyboard && ionic.keyboard.hide && ionic.keyboard.hide(); - }); - - $rootScope.$on('$ionicHistory.change', function(e, data) { - if (!data) return null; - - var viewHistory = $ionicHistory.viewHistory(); - - var hist = (data.historyId ? viewHistory.histories[ data.historyId ] : null); - if (hist && hist.cursor > -1 && hist.cursor < hist.stack.length) { - // the history they're going to already exists - // go to it's last view in its stack - var view = hist.stack[ hist.cursor ]; - return view.go(data); - } - - // this history does not have a URL, but it does have a uiSref - // figure out its URL from the uiSref - if (!data.url && data.uiSref) { - data.url = $state.href(data.uiSref); - } - - if (data.url) { - // don't let it start with a #, messes with $location.url() - if (data.url.indexOf('#') === 0) { - data.url = data.url.replace('#', ''); - } - if (data.url !== $location.url()) { - // we've got a good URL, ready GO! - $location.url(data.url); - } - } - }); - - $rootScope.$ionicGoBack = function(backCount) { - $ionicHistory.goBack(backCount); - }; - - // Set the document title when a new view is shown - $rootScope.$on('$ionicView.afterEnter', function(ev, data) { - if (data && data.title) { - $document[0].title = data.title; - } - }); - - // Triggered when devices with a hardware back button (Android) is clicked by the user - // This is a Cordova/Phonegap platform specifc method - function onHardwareBackButton(e) { - var backView = $ionicHistory.backView(); - if (backView) { - // there is a back view, go to it - backView.go(); - } else { - // there is no back view, so close the app instead - ionic.Platform.exitApp(); - } - e.preventDefault(); - return false; - } - $ionicPlatform.registerBackButtonAction( - onHardwareBackButton, - IONIC_BACK_PRIORITY.view - ); - -}]); - -/** - * @ngdoc provider - * @name $ionicConfigProvider - * @module ionic - * @description - * Ionic automatically takes platform configurations into account to adjust things like what - * transition style to use and whether tab icons should show on the top or bottom. For example, - * iOS will move forward by transitioning the entering view from right to center and the leaving - * view from center to left. However, Android will transition with the entering view going from - * bottom to center, covering the previous view, which remains stationary. It should be noted - * that when a platform is not iOS or Android, then it'll default to iOS. So if you are - * developing on a desktop browser, it's going to take on iOS default configs. - * - * These configs can be changed using the `$ionicConfigProvider` during the configuration phase - * of your app. Additionally, `$ionicConfig` can also set and get config values during the run - * phase and within the app itself. - * - * By default, all base config variables are set to `'platform'`, which means it'll take on the - * default config of the platform on which it's running. Config variables can be set at this - * level so all platforms follow the same setting, rather than its platform config. - * The following code would set the same config variable for all platforms: - * - * ```js - * $ionicConfigProvider.views.maxCache(10); - * ``` - * - * Additionally, each platform can have it's own config within the `$ionicConfigProvider.platform` - * property. The config below would only apply to Android devices. - * - * ```js - * $ionicConfigProvider.platform.android.views.maxCache(5); - * ``` - * - * @usage - * ```js - * var myApp = angular.module('reallyCoolApp', ['ionic']); - * - * myApp.config(function($ionicConfigProvider) { - * $ionicConfigProvider.views.maxCache(5); - * - * // note that you can also chain configs - * $ionicConfigProvider.backButton.text('Go Back').icon('ion-chevron-left'); - * }); - * ``` - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#views.transition - * @description Animation style when transitioning between views. Default `platform`. - * - * @param {string} transition Which style of view transitioning to use. - * - * * `platform`: Dynamically choose the correct transition style depending on the platform - * the app is running from. If the platform is not `ios` or `android` then it will default - * to `ios`. - * * `ios`: iOS style transition. - * * `android`: Android style transition. - * * `none`: Do not perform animated transitions. - * - * @returns {string} value - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#views.maxCache - * @description Maximum number of view elements to cache in the DOM. When the max number is - * exceeded, the view with the longest time period since it was accessed is removed. Views that - * stay in the DOM cache the view's scope, current state, and scroll position. The scope is - * disconnected from the `$watch` cycle when it is cached and reconnected when it enters again. - * When the maximum cache is `0`, the leaving view's element will be removed from the DOM after - * each view transition, and the next time the same view is shown, it will have to re-compile, - * attach to the DOM, and link the element again. This disables caching, in effect. - * @param {number} maxNumber Maximum number of views to retain. Default `10`. - * @returns {number} How many views Ionic will hold onto until the a view is removed. - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#views.forwardCache - * @description By default, when navigating, views that were recently visited are cached, and - * the same instance data and DOM elements are referenced when navigating back. However, when - * navigating back in the history, the "forward" views are removed from the cache. If you - * navigate forward to the same view again, it'll create a new DOM element and controller - * instance. Basically, any forward views are reset each time. Set this config to `true` to have - * forward views cached and not reset on each load. - * @param {boolean} value - * @returns {boolean} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#scrolling.jsScrolling - * @description Whether to use JS or Native scrolling. Defaults to native scrolling. Setting this to - * `true` has the same effect as setting each `ion-content` to have `overflow-scroll='false'`. - * @param {boolean} value Defaults to `false` as of Ionic 1.2 - * @returns {boolean} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#backButton.icon - * @description Back button icon. - * @param {string} value - * @returns {string} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#backButton.text - * @description Back button text. - * @param {string} value Defaults to `Back`. - * @returns {string} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#backButton.previousTitleText - * @description If the previous title text should become the back button text. This - * is the default for iOS. - * @param {boolean} value - * @returns {boolean} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#form.checkbox - * @description Checkbox style. Android defaults to `square` and iOS defaults to `circle`. - * @param {string} value - * @returns {string} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#form.toggle - * @description Toggle item style. Android defaults to `small` and iOS defaults to `large`. - * @param {string} value - * @returns {string} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#spinner.icon - * @description Default spinner icon to use. - * @param {string} value Can be: `android`, `ios`, `ios-small`, `bubbles`, `circles`, `crescent`, - * `dots`, `lines`, `ripple`, or `spiral`. - * @returns {string} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#tabs.style - * @description Tab style. Android defaults to `striped` and iOS defaults to `standard`. - * @param {string} value Available values include `striped` and `standard`. - * @returns {string} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#tabs.position - * @description Tab position. Android defaults to `top` and iOS defaults to `bottom`. - * @param {string} value Available values include `top` and `bottom`. - * @returns {string} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#templates.maxPrefetch - * @description Sets the maximum number of templates to prefetch from the templateUrls defined in - * $stateProvider.state. If set to `0`, the user will have to wait - * for a template to be fetched the first time when navigating to a new page. Default `30`. - * @param {integer} value Max number of template to prefetch from the templateUrls defined in - * `$stateProvider.state()`. - * @returns {integer} - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#navBar.alignTitle - * @description Which side of the navBar to align the title. Default `center`. - * - * @param {string} value side of the navBar to align the title. - * - * * `platform`: Dynamically choose the correct title style depending on the platform - * the app is running from. If the platform is `ios`, it will default to `center`. - * If the platform is `android`, it will default to `left`. If the platform is not - * `ios` or `android`, it will default to `center`. - * - * * `left`: Left align the title in the navBar - * * `center`: Center align the title in the navBar - * * `right`: Right align the title in the navBar. - * - * @returns {string} value - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#navBar.positionPrimaryButtons - * @description Which side of the navBar to align the primary navBar buttons. Default `left`. - * - * @param {string} value side of the navBar to align the primary navBar buttons. - * - * * `platform`: Dynamically choose the correct title style depending on the platform - * the app is running from. If the platform is `ios`, it will default to `left`. - * If the platform is `android`, it will default to `right`. If the platform is not - * `ios` or `android`, it will default to `left`. - * - * * `left`: Left align the primary navBar buttons in the navBar - * * `right`: Right align the primary navBar buttons in the navBar. - * - * @returns {string} value - */ - -/** - * @ngdoc method - * @name $ionicConfigProvider#navBar.positionSecondaryButtons - * @description Which side of the navBar to align the secondary navBar buttons. Default `right`. - * - * @param {string} value side of the navBar to align the secondary navBar buttons. - * - * * `platform`: Dynamically choose the correct title style depending on the platform - * the app is running from. If the platform is `ios`, it will default to `right`. - * If the platform is `android`, it will default to `right`. If the platform is not - * `ios` or `android`, it will default to `right`. - * - * * `left`: Left align the secondary navBar buttons in the navBar - * * `right`: Right align the secondary navBar buttons in the navBar. - * - * @returns {string} value - */ - -IonicModule -.provider('$ionicConfig', function() { - - var provider = this; - provider.platform = {}; - var PLATFORM = 'platform'; - - var configProperties = { - views: { - maxCache: PLATFORM, - forwardCache: PLATFORM, - transition: PLATFORM, - swipeBackEnabled: PLATFORM, - swipeBackHitWidth: PLATFORM - }, - navBar: { - alignTitle: PLATFORM, - positionPrimaryButtons: PLATFORM, - positionSecondaryButtons: PLATFORM, - transition: PLATFORM - }, - backButton: { - icon: PLATFORM, - text: PLATFORM, - previousTitleText: PLATFORM - }, - form: { - checkbox: PLATFORM, - toggle: PLATFORM - }, - scrolling: { - jsScrolling: PLATFORM - }, - spinner: { - icon: PLATFORM - }, - tabs: { - style: PLATFORM, - position: PLATFORM - }, - templates: { - maxPrefetch: PLATFORM - }, - platform: {} - }; - createConfig(configProperties, provider, ''); - - - - // Default - // ------------------------- - setPlatformConfig('default', { - - views: { - maxCache: 10, - forwardCache: false, - transition: 'ios', - swipeBackEnabled: true, - swipeBackHitWidth: 45 - }, - - navBar: { - alignTitle: 'center', - positionPrimaryButtons: 'left', - positionSecondaryButtons: 'right', - transition: 'view' - }, - - backButton: { - icon: 'ion-ios-arrow-back', - text: 'Back', - previousTitleText: true - }, - - form: { - checkbox: 'circle', - toggle: 'large' - }, - - scrolling: { - jsScrolling: true - }, - - spinner: { - icon: 'ios' - }, - - tabs: { - style: 'standard', - position: 'bottom' - }, - - templates: { - maxPrefetch: 30 - } - - }); - - - - // iOS (it is the default already) - // ------------------------- - setPlatformConfig('ios', {}); - - - - // Android - // ------------------------- - setPlatformConfig('android', { - - views: { - transition: 'android', - swipeBackEnabled: false - }, - - navBar: { - alignTitle: 'left', - positionPrimaryButtons: 'right', - positionSecondaryButtons: 'right' - }, - - backButton: { - icon: 'ion-android-arrow-back', - text: false, - previousTitleText: false - }, - - form: { - checkbox: 'square', - toggle: 'small' - }, - - spinner: { - icon: 'android' - }, - - tabs: { - style: 'striped', - position: 'top' - }, - - scrolling: { - jsScrolling: false - } - }); - - // Windows Phone - // ------------------------- - setPlatformConfig('windowsphone', { - //scrolling: { - // jsScrolling: false - //} - spinner: { - icon: 'android' - } - }); - - - provider.transitions = { - views: {}, - navBar: {} - }; - - - // iOS Transitions - // ----------------------- - provider.transitions.views.ios = function(enteringEle, leavingEle, direction, shouldAnimate) { - - function setStyles(ele, opacity, x, boxShadowOpacity) { - var css = {}; - css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0; - css.opacity = opacity; - if (boxShadowOpacity > -1) { - css.boxShadow = '0 0 10px rgba(0,0,0,' + (d.shouldAnimate ? boxShadowOpacity * 0.45 : 0.3) + ')'; - } - css[ionic.CSS.TRANSFORM] = 'translate3d(' + x + '%,0,0)'; - ionic.DomUtil.cachedStyles(ele, css); - } - - var d = { - run: function(step) { - if (direction == 'forward') { - setStyles(enteringEle, 1, (1 - step) * 99, 1 - step); // starting at 98% prevents a flicker - setStyles(leavingEle, (1 - 0.1 * step), step * -33, -1); - - } else if (direction == 'back') { - setStyles(enteringEle, (1 - 0.1 * (1 - step)), (1 - step) * -33, -1); - setStyles(leavingEle, 1, step * 100, 1 - step); - - } else { - // swap, enter, exit - setStyles(enteringEle, 1, 0, -1); - setStyles(leavingEle, 0, 0, -1); - } - }, - shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back') - }; - - return d; - }; - - provider.transitions.navBar.ios = function(enteringHeaderBar, leavingHeaderBar, direction, shouldAnimate) { - - function setStyles(ctrl, opacity, titleX, backTextX) { - var css = {}; - css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : '0ms'; - css.opacity = opacity === 1 ? '' : opacity; - - ctrl.setCss('buttons-left', css); - ctrl.setCss('buttons-right', css); - ctrl.setCss('back-button', css); - - css[ionic.CSS.TRANSFORM] = 'translate3d(' + backTextX + 'px,0,0)'; - ctrl.setCss('back-text', css); - - css[ionic.CSS.TRANSFORM] = 'translate3d(' + titleX + 'px,0,0)'; - ctrl.setCss('title', css); - } - - function enter(ctrlA, ctrlB, step) { - if (!ctrlA || !ctrlB) return; - var titleX = (ctrlA.titleTextX() + ctrlA.titleWidth()) * (1 - step); - var backTextX = (ctrlB && (ctrlB.titleTextX() - ctrlA.backButtonTextLeft()) * (1 - step)) || 0; - setStyles(ctrlA, step, titleX, backTextX); - } - - function leave(ctrlA, ctrlB, step) { - if (!ctrlA || !ctrlB) return; - var titleX = (-(ctrlA.titleTextX() - ctrlB.backButtonTextLeft()) - (ctrlA.titleLeftRight())) * step; - setStyles(ctrlA, 1 - step, titleX, 0); - } - - var d = { - run: function(step) { - var enteringHeaderCtrl = enteringHeaderBar.controller(); - var leavingHeaderCtrl = leavingHeaderBar && leavingHeaderBar.controller(); - if (d.direction == 'back') { - leave(enteringHeaderCtrl, leavingHeaderCtrl, 1 - step); - enter(leavingHeaderCtrl, enteringHeaderCtrl, 1 - step); - } else { - enter(enteringHeaderCtrl, leavingHeaderCtrl, step); - leave(leavingHeaderCtrl, enteringHeaderCtrl, step); - } - }, - direction: direction, - shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back') - }; - - return d; - }; - - - // Android Transitions - // ----------------------- - - provider.transitions.views.android = function(enteringEle, leavingEle, direction, shouldAnimate) { - shouldAnimate = shouldAnimate && (direction == 'forward' || direction == 'back'); - - function setStyles(ele, x) { - var css = {}; - css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0; - css[ionic.CSS.TRANSFORM] = 'translate3d(' + x + '%,0,0)'; - ionic.DomUtil.cachedStyles(ele, css); - } - - var d = { - run: function(step) { - if (direction == 'forward') { - setStyles(enteringEle, (1 - step) * 99); // starting at 98% prevents a flicker - setStyles(leavingEle, step * -100); - - } else if (direction == 'back') { - setStyles(enteringEle, (1 - step) * -100); - setStyles(leavingEle, step * 100); - - } else { - // swap, enter, exit - setStyles(enteringEle, 0); - setStyles(leavingEle, 0); - } - }, - shouldAnimate: shouldAnimate - }; - - return d; - }; - - provider.transitions.navBar.android = function(enteringHeaderBar, leavingHeaderBar, direction, shouldAnimate) { - - function setStyles(ctrl, opacity) { - if (!ctrl) return; - var css = {}; - css.opacity = opacity === 1 ? '' : opacity; - - ctrl.setCss('buttons-left', css); - ctrl.setCss('buttons-right', css); - ctrl.setCss('back-button', css); - ctrl.setCss('back-text', css); - ctrl.setCss('title', css); - } - - return { - run: function(step) { - setStyles(enteringHeaderBar.controller(), step); - setStyles(leavingHeaderBar && leavingHeaderBar.controller(), 1 - step); - }, - shouldAnimate: shouldAnimate && (direction == 'forward' || direction == 'back') - }; - }; - - - // No Transition - // ----------------------- - - provider.transitions.views.none = function(enteringEle, leavingEle) { - return { - run: function(step) { - provider.transitions.views.android(enteringEle, leavingEle, false, false).run(step); - }, - shouldAnimate: false - }; - }; - - provider.transitions.navBar.none = function(enteringHeaderBar, leavingHeaderBar) { - return { - run: function(step) { - provider.transitions.navBar.ios(enteringHeaderBar, leavingHeaderBar, false, false).run(step); - provider.transitions.navBar.android(enteringHeaderBar, leavingHeaderBar, false, false).run(step); - }, - shouldAnimate: false - }; - }; - - - // private: used to set platform configs - function setPlatformConfig(platformName, platformConfigs) { - configProperties.platform[platformName] = platformConfigs; - provider.platform[platformName] = {}; - - addConfig(configProperties, configProperties.platform[platformName]); - - createConfig(configProperties.platform[platformName], provider.platform[platformName], ''); - } - - - // private: used to recursively add new platform configs - function addConfig(configObj, platformObj) { - for (var n in configObj) { - if (n != PLATFORM && configObj.hasOwnProperty(n)) { - if (angular.isObject(configObj[n])) { - if (!isDefined(platformObj[n])) { - platformObj[n] = {}; - } - addConfig(configObj[n], platformObj[n]); - - } else if (!isDefined(platformObj[n])) { - platformObj[n] = null; - } - } - } - } - - - // private: create methods for each config to get/set - function createConfig(configObj, providerObj, platformPath) { - forEach(configObj, function(value, namespace) { - - if (angular.isObject(configObj[namespace])) { - // recursively drill down the config object so we can create a method for each one - providerObj[namespace] = {}; - createConfig(configObj[namespace], providerObj[namespace], platformPath + '.' + namespace); - - } else { - // create a method for the provider/config methods that will be exposed - providerObj[namespace] = function(newValue) { - if (arguments.length) { - configObj[namespace] = newValue; - return providerObj; - } - if (configObj[namespace] == PLATFORM) { - // if the config is set to 'platform', then get this config's platform value - var platformConfig = stringObj(configProperties.platform, ionic.Platform.platform() + platformPath + '.' + namespace); - if (platformConfig || platformConfig === false) { - return platformConfig; - } - // didnt find a specific platform config, now try the default - return stringObj(configProperties.platform, 'default' + platformPath + '.' + namespace); - } - return configObj[namespace]; - }; - } - - }); - } - - function stringObj(obj, str) { - str = str.split("."); - for (var i = 0; i < str.length; i++) { - if (obj && isDefined(obj[str[i]])) { - obj = obj[str[i]]; - } else { - return null; - } - } - return obj; - } - - provider.setPlatformConfig = setPlatformConfig; - - - // private: Service definition for internal Ionic use - /** - * @ngdoc service - * @name $ionicConfig - * @module ionic - * @private - */ - provider.$get = function() { - return provider; - }; -}) -// Fix for URLs in Cordova apps on Windows Phone -// http://blogs.msdn.com/b/msdn_answers/archive/2015/02/10/ -// running-cordova-apps-on-windows-and-windows-phone-8-1-using-ionic-angularjs-and-other-frameworks.aspx -.config(['$compileProvider', function($compileProvider) { - $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|sms|tel|geo|ftp|mailto|file|ghttps?|ms-appx-web|ms-appx|x-wmapp0):/); - $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|content|blob|ms-appx|ms-appx-web|x-wmapp0):|data:image\//); -}]); - - -var LOADING_TPL = - '
' + - '
' + - '
' + - '
'; - -var LOADING_HIDE_DEPRECATED = '$ionicLoading instance.hide() has been deprecated. Use $ionicLoading.hide().'; -var LOADING_SHOW_DEPRECATED = '$ionicLoading instance.show() has been deprecated. Use $ionicLoading.show().'; -var LOADING_SET_DEPRECATED = '$ionicLoading instance.setContent() has been deprecated. Use $ionicLoading.show({ template: \'my content\' }).'; - -/** - * @ngdoc service - * @name $ionicLoading - * @module ionic - * @description - * An overlay that can be used to indicate activity while blocking user - * interaction. - * - * @usage - * ```js - * angular.module('LoadingApp', ['ionic']) - * .controller('LoadingCtrl', function($scope, $ionicLoading) { - * $scope.show = function() { - * $ionicLoading.show({ - * template: 'Loading...' - * }); - * }; - * $scope.hide = function(){ - * $ionicLoading.hide(); - * }; - * }); - * ``` - */ -/** - * @ngdoc object - * @name $ionicLoadingConfig - * @module ionic - * @description - * Set the default options to be passed to the {@link ionic.service:$ionicLoading} service. - * - * @usage - * ```js - * var app = angular.module('myApp', ['ionic']) - * app.constant('$ionicLoadingConfig', { - * template: 'Default Loading Template...' - * }); - * app.controller('AppCtrl', function($scope, $ionicLoading) { - * $scope.showLoading = function() { - * $ionicLoading.show(); //options default to values in $ionicLoadingConfig - * }; - * }); - * ``` - */ -IonicModule -.constant('$ionicLoadingConfig', { - template: '' -}) -.factory('$ionicLoading', [ - '$ionicLoadingConfig', - '$ionicBody', - '$ionicTemplateLoader', - '$ionicBackdrop', - '$timeout', - '$q', - '$log', - '$compile', - '$ionicPlatform', - '$rootScope', - 'IONIC_BACK_PRIORITY', -function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop, $timeout, $q, $log, $compile, $ionicPlatform, $rootScope, IONIC_BACK_PRIORITY) { - - var loaderInstance; - //default values - var deregisterBackAction = noop; - var deregisterStateListener1 = noop; - var deregisterStateListener2 = noop; - var loadingShowDelay = $q.when(); - - return { - /** - * @ngdoc method - * @name $ionicLoading#show - * @description Shows a loading indicator. If the indicator is already shown, - * it will set the options given and keep the indicator shown. Note: While this - * function still returns an $ionicLoading instance for backwards compatiblity, - * its use has been deprecated. - * @param {object} opts The options for the loading indicator. Available properties: - * - `{string=}` `template` The html content of the indicator. - * - `{string=}` `templateUrl` The url of an html template to load as the content of the indicator. - * - `{object=}` `scope` The scope to be a child of. Default: creates a child of $rootScope. - * - `{boolean=}` `noBackdrop` Whether to hide the backdrop. By default it will be shown. - * - `{boolean=}` `hideOnStateChange` Whether to hide the loading spinner when navigating - * to a new state. Default false. - * - `{number=}` `delay` How many milliseconds to delay showing the indicator. By default there is no delay. - * - `{number=}` `duration` How many milliseconds to wait until automatically - * hiding the indicator. By default, the indicator will be shown until `.hide()` is called. - */ - show: showLoader, - /** - * @ngdoc method - * @name $ionicLoading#hide - * @description Hides the loading indicator, if shown. - */ - hide: hideLoader, - /** - * @private for testing - */ - _getLoader: getLoader - }; - - function getLoader() { - if (!loaderInstance) { - loaderInstance = $ionicTemplateLoader.compile({ - template: LOADING_TPL, - appendTo: $ionicBody.get() - }) - .then(function(self) { - self.show = function(options) { - var templatePromise = options.templateUrl ? - $ionicTemplateLoader.load(options.templateUrl) : - //options.content: deprecated - $q.when(options.template || options.content || ''); - - self.scope = options.scope || self.scope; - - if (!self.isShown) { - //options.showBackdrop: deprecated - self.hasBackdrop = !options.noBackdrop && options.showBackdrop !== false; - if (self.hasBackdrop) { - $ionicBackdrop.retain(); - $ionicBackdrop.getElement().addClass('backdrop-loading'); - } - } - - if (options.duration) { - $timeout.cancel(self.durationTimeout); - self.durationTimeout = $timeout( - angular.bind(self, self.hide), - +options.duration - ); - } - - deregisterBackAction(); - //Disable hardware back button while loading - deregisterBackAction = $ionicPlatform.registerBackButtonAction( - noop, - IONIC_BACK_PRIORITY.loading - ); - - templatePromise.then(function(html) { - if (html) { - var loading = self.element.children(); - loading.html(html); - $compile(loading.contents())(self.scope); - } - - //Don't show until template changes - if (self.isShown) { - self.element.addClass('visible'); - ionic.requestAnimationFrame(function() { - if (self.isShown) { - self.element.addClass('active'); - $ionicBody.addClass('loading-active'); - } - }); - } - }); - - self.isShown = true; - }; - self.hide = function() { - - deregisterBackAction(); - if (self.isShown) { - if (self.hasBackdrop) { - $ionicBackdrop.release(); - $ionicBackdrop.getElement().removeClass('backdrop-loading'); - } - self.element.removeClass('active'); - $ionicBody.removeClass('loading-active'); - self.element.removeClass('visible'); - ionic.requestAnimationFrame(function() { - !self.isShown && self.element.removeClass('visible'); - }); - } - $timeout.cancel(self.durationTimeout); - self.isShown = false; - var loading = self.element.children(); - loading.html(""); - }; - - return self; - }); - } - return loaderInstance; - } - - function showLoader(options) { - options = extend({}, $ionicLoadingConfig || {}, options || {}); - var delay = options.delay || options.showDelay || 0; - - deregisterStateListener1(); - deregisterStateListener2(); - if (options.hideOnStateChange) { - deregisterStateListener1 = $rootScope.$on('$stateChangeSuccess', hideLoader); - deregisterStateListener2 = $rootScope.$on('$stateChangeError', hideLoader); - } - - //If loading.show() was called previously, cancel it and show with our new options - $timeout.cancel(loadingShowDelay); - loadingShowDelay = $timeout(noop, delay); - loadingShowDelay.then(getLoader).then(function(loader) { - return loader.show(options); - }); - - return { - hide: function deprecatedHide() { - $log.error(LOADING_HIDE_DEPRECATED); - return hideLoader.apply(this, arguments); - }, - show: function deprecatedShow() { - $log.error(LOADING_SHOW_DEPRECATED); - return showLoader.apply(this, arguments); - }, - setContent: function deprecatedSetContent(content) { - $log.error(LOADING_SET_DEPRECATED); - return getLoader().then(function(loader) { - loader.show({ template: content }); - }); - } - }; - } - - function hideLoader() { - deregisterStateListener1(); - deregisterStateListener2(); - $timeout.cancel(loadingShowDelay); - getLoader().then(function(loader) { - loader.hide(); - }); - } -}]); - -/** - * @ngdoc service - * @name $ionicModal - * @module ionic - * @description - * - * Related: {@link ionic.controller:ionicModal ionicModal controller}. - * - * The Modal is a content pane that can go over the user's main view - * temporarily. Usually used for making a choice or editing an item. - * - * Put the content of the modal inside of an `` element. - * - * **Notes:** - * - A modal will broadcast 'modal.shown', 'modal.hidden', and 'modal.removed' events from its originating - * scope, passing in itself as an event argument. Both the modal.removed and modal.hidden events are - * called when the modal is removed. - * - * - This example assumes your modal is in your main index file or another template file. If it is in its own - * template file, remove the script tags and call it by file name. - * - * @usage - * ```html - * - * ``` - * ```js - * angular.module('testApp', ['ionic']) - * .controller('MyController', function($scope, $ionicModal) { - * $ionicModal.fromTemplateUrl('my-modal.html', { - * scope: $scope, - * animation: 'slide-in-up' - * }).then(function(modal) { - * $scope.modal = modal; - * }); - * $scope.openModal = function() { - * $scope.modal.show(); - * }; - * $scope.closeModal = function() { - * $scope.modal.hide(); - * }; - * //Cleanup the modal when we're done with it! - * $scope.$on('$destroy', function() { - * $scope.modal.remove(); - * }); - * // Execute action on hide modal - * $scope.$on('modal.hidden', function() { - * // Execute action - * }); - * // Execute action on remove modal - * $scope.$on('modal.removed', function() { - * // Execute action - * }); - * }); - * ``` - */ -IonicModule -.factory('$ionicModal', [ - '$rootScope', - '$ionicBody', - '$compile', - '$timeout', - '$ionicPlatform', - '$ionicTemplateLoader', - '$$q', - '$log', - '$ionicClickBlock', - '$window', - 'IONIC_BACK_PRIORITY', -function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTemplateLoader, $$q, $log, $ionicClickBlock, $window, IONIC_BACK_PRIORITY) { - - /** - * @ngdoc controller - * @name ionicModal - * @module ionic - * @description - * Instantiated by the {@link ionic.service:$ionicModal} service. - * - * Be sure to call [remove()](#remove) when you are done with each modal - * to clean it up and avoid memory leaks. - * - * Note: a modal will broadcast 'modal.shown', 'modal.hidden', and 'modal.removed' events from its originating - * scope, passing in itself as an event argument. Note: both modal.removed and modal.hidden are - * called when the modal is removed. - */ - var ModalView = ionic.views.Modal.inherit({ - /** - * @ngdoc method - * @name ionicModal#initialize - * @description Creates a new modal controller instance. - * @param {object} options An options object with the following properties: - * - `{object=}` `scope` The scope to be a child of. - * Default: creates a child of $rootScope. - * - `{string=}` `animation` The animation to show & hide with. - * Default: 'slide-in-up' - * - `{boolean=}` `focusFirstInput` Whether to autofocus the first input of - * the modal when shown. Will only show the keyboard on iOS, to force the keyboard to show - * on Android, please use the [Ionic keyboard plugin](https://github.com/driftyco/ionic-plugin-keyboard#keyboardshow). - * Default: false. - * - `{boolean=}` `backdropClickToClose` Whether to close the modal on clicking the backdrop. - * Default: true. - * - `{boolean=}` `hardwareBackButtonClose` Whether the modal can be closed using the hardware - * back button on Android and similar devices. Default: true. - */ - initialize: function(opts) { - ionic.views.Modal.prototype.initialize.call(this, opts); - this.animation = opts.animation || 'slide-in-up'; - }, - - /** - * @ngdoc method - * @name ionicModal#show - * @description Show this modal instance. - * @returns {promise} A promise which is resolved when the modal is finished animating in. - */ - show: function(target) { - var self = this; - - if (self.scope.$$destroyed) { - $log.error('Cannot call ' + self.viewType + '.show() after remove(). Please create a new ' + self.viewType + ' instance.'); - return $$q.when(); - } - - // on iOS, clicks will sometimes bleed through/ghost click on underlying - // elements - $ionicClickBlock.show(600); - stack.add(self); - - var modalEl = jqLite(self.modalEl); - - self.el.classList.remove('hide'); - $timeout(function() { - if (!self._isShown) return; - $ionicBody.addClass(self.viewType + '-open'); - }, 400, false); - - if (!self.el.parentElement) { - modalEl.addClass(self.animation); - $ionicBody.append(self.el); - } - - // if modal was closed while the keyboard was up, reset scroll view on - // next show since we can only resize it once it's visible - var scrollCtrl = modalEl.data('$$ionicScrollController'); - scrollCtrl && scrollCtrl.resize(); - - if (target && self.positionView) { - self.positionView(target, modalEl); - // set up a listener for in case the window size changes - - self._onWindowResize = function() { - if (self._isShown) self.positionView(target, modalEl); - }; - ionic.on('resize', self._onWindowResize, window); - } - - modalEl.addClass('ng-enter active') - .removeClass('ng-leave ng-leave-active'); - - self._isShown = true; - self._deregisterBackButton = $ionicPlatform.registerBackButtonAction( - self.hardwareBackButtonClose ? angular.bind(self, self.hide) : noop, - IONIC_BACK_PRIORITY.modal - ); - - ionic.views.Modal.prototype.show.call(self); - - $timeout(function() { - if (!self._isShown) return; - modalEl.addClass('ng-enter-active'); - ionic.trigger('resize'); - self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.shown', self); - self.el.classList.add('active'); - self.scope.$broadcast('$ionicHeader.align'); - self.scope.$broadcast('$ionicFooter.align'); - }, 20); - - return $timeout(function() { - if (!self._isShown) return; - self.$el.on('touchmove', function(e) { - //Don't allow scrolling while open by dragging on backdrop - var isInScroll = ionic.DomUtil.getParentOrSelfWithClass(e.target, 'scroll'); - if (!isInScroll) { - e.preventDefault(); - } - }); - //After animating in, allow hide on backdrop click - self.$el.on('click', function(e) { - if (self.backdropClickToClose && e.target === self.el && stack.isHighest(self)) { - self.hide(); - } - }); - }, 400); - }, - - /** - * @ngdoc method - * @name ionicModal#hide - * @description Hide this modal instance. - * @returns {promise} A promise which is resolved when the modal is finished animating out. - */ - hide: function() { - var self = this; - var modalEl = jqLite(self.modalEl); - - // on iOS, clicks will sometimes bleed through/ghost click on underlying - // elements - $ionicClickBlock.show(600); - stack.remove(self); - - self.el.classList.remove('active'); - modalEl.addClass('ng-leave'); - - $timeout(function() { - if (self._isShown) return; - modalEl.addClass('ng-leave-active') - .removeClass('ng-enter ng-enter-active active'); - }, 20, false); - - self.$el.off('click'); - self._isShown = false; - self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.hidden', self); - self._deregisterBackButton && self._deregisterBackButton(); - - ionic.views.Modal.prototype.hide.call(self); - - // clean up event listeners - if (self.positionView) { - ionic.off('resize', self._onWindowResize, window); - } - - return $timeout(function() { - $ionicBody.removeClass(self.viewType + '-open'); - self.el.classList.add('hide'); - }, self.hideDelay || 320); - }, - - /** - * @ngdoc method - * @name ionicModal#remove - * @description Remove this modal instance from the DOM and clean up. - * @returns {promise} A promise which is resolved when the modal is finished animating out. - */ - remove: function() { - var self = this; - self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.removed', self); - - return self.hide().then(function() { - self.scope.$destroy(); - self.$el.remove(); - }); - }, - - /** - * @ngdoc method - * @name ionicModal#isShown - * @returns boolean Whether this modal is currently shown. - */ - isShown: function() { - return !!this._isShown; - } - }); - - var createModal = function(templateString, options) { - // Create a new scope for the modal - var scope = options.scope && options.scope.$new() || $rootScope.$new(true); - - options.viewType = options.viewType || 'modal'; - - extend(scope, { - $hasHeader: false, - $hasSubheader: false, - $hasFooter: false, - $hasSubfooter: false, - $hasTabs: false, - $hasTabsTop: false - }); - - // Compile the template - var element = $compile('' + templateString + '')(scope); - - options.$el = element; - options.el = element[0]; - options.modalEl = options.el.querySelector('.' + options.viewType); - var modal = new ModalView(options); - - modal.scope = scope; - - // If this wasn't a defined scope, we can assign the viewType to the isolated scope - // we created - if (!options.scope) { - scope[ options.viewType ] = modal; - } - - return modal; - }; - - var modalStack = []; - var stack = { - add: function(modal) { - modalStack.push(modal); - }, - remove: function(modal) { - var index = modalStack.indexOf(modal); - if (index > -1 && index < modalStack.length) { - modalStack.splice(index, 1); - } - }, - isHighest: function(modal) { - var index = modalStack.indexOf(modal); - return (index > -1 && index === modalStack.length - 1); - } - }; - - return { - /** - * @ngdoc method - * @name $ionicModal#fromTemplate - * @param {string} templateString The template string to use as the modal's - * content. - * @param {object} options Options to be passed {@link ionic.controller:ionicModal#initialize ionicModal#initialize} method. - * @returns {object} An instance of an {@link ionic.controller:ionicModal} - * controller. - */ - fromTemplate: function(templateString, options) { - var modal = createModal(templateString, options || {}); - return modal; - }, - /** - * @ngdoc method - * @name $ionicModal#fromTemplateUrl - * @param {string} templateUrl The url to load the template from. - * @param {object} options Options to be passed {@link ionic.controller:ionicModal#initialize ionicModal#initialize} method. - * options object. - * @returns {promise} A promise that will be resolved with an instance of - * an {@link ionic.controller:ionicModal} controller. - */ - fromTemplateUrl: function(url, options, _) { - var cb; - //Deprecated: allow a callback as second parameter. Now we return a promise. - if (angular.isFunction(options)) { - cb = options; - options = _; - } - return $ionicTemplateLoader.load(url).then(function(templateString) { - var modal = createModal(templateString, options || {}); - cb && cb(modal); - return modal; - }); - }, - - stack: stack - }; -}]); - - -/** - * @ngdoc service - * @name $ionicNavBarDelegate - * @module ionic - * @description - * Delegate for controlling the {@link ionic.directive:ionNavBar} directive. - * - * @usage - * - * ```html - * - * - * - * - * - * ``` - * ```js - * function MyCtrl($scope, $ionicNavBarDelegate) { - * $scope.setNavTitle = function(title) { - * $ionicNavBarDelegate.title(title); - * } - * } - * ``` - */ -IonicModule -.service('$ionicNavBarDelegate', ionic.DelegateService([ - /** - * @ngdoc method - * @name $ionicNavBarDelegate#align - * @description Aligns the title with the buttons in a given direction. - * @param {string=} direction The direction to the align the title text towards. - * Available: 'left', 'right', 'center'. Default: 'center'. - */ - 'align', - /** - * @ngdoc method - * @name $ionicNavBarDelegate#showBackButton - * @description - * Set/get whether the {@link ionic.directive:ionNavBackButton} is shown - * (if it exists and there is a previous view that can be navigated to). - * @param {boolean=} show Whether to show the back button. - * @returns {boolean} Whether the back button is shown. - */ - 'showBackButton', - /** - * @ngdoc method - * @name $ionicNavBarDelegate#showBar - * @description - * Set/get whether the {@link ionic.directive:ionNavBar} is shown. - * @param {boolean} show Whether to show the bar. - * @returns {boolean} Whether the bar is shown. - */ - 'showBar', - /** - * @ngdoc method - * @name $ionicNavBarDelegate#title - * @description - * Set the title for the {@link ionic.directive:ionNavBar}. - * @param {string} title The new title to show. - */ - 'title', - - // DEPRECATED, as of v1.0.0-beta14 ------- - 'changeTitle', - 'setTitle', - 'getTitle', - 'back', - 'getPreviousTitle' - // END DEPRECATED ------- -])); - - -IonicModule -.service('$ionicNavViewDelegate', ionic.DelegateService([ - 'clearCache' -])); - - - -/** - * @ngdoc service - * @name $ionicPlatform - * @module ionic - * @description - * An angular abstraction of {@link ionic.utility:ionic.Platform}. - * - * Used to detect the current platform, as well as do things like override the - * Android back button in PhoneGap/Cordova. - */ -IonicModule -.constant('IONIC_BACK_PRIORITY', { - view: 100, - sideMenu: 150, - modal: 200, - actionSheet: 300, - popup: 400, - loading: 500 -}) -.provider('$ionicPlatform', function() { - return { - $get: ['$q', '$ionicScrollDelegate', function($q, $ionicScrollDelegate) { - var self = { - - /** - * @ngdoc method - * @name $ionicPlatform#onHardwareBackButton - * @description - * Some platforms have a hardware back button, so this is one way to - * bind to it. - * @param {function} callback the callback to trigger when this event occurs - */ - onHardwareBackButton: function(cb) { - ionic.Platform.ready(function() { - document.addEventListener('backbutton', cb, false); - }); - }, - - /** - * @ngdoc method - * @name $ionicPlatform#offHardwareBackButton - * @description - * Remove an event listener for the backbutton. - * @param {function} callback The listener function that was - * originally bound. - */ - offHardwareBackButton: function(fn) { - ionic.Platform.ready(function() { - document.removeEventListener('backbutton', fn); - }); - }, - - /** - * @ngdoc method - * @name $ionicPlatform#registerBackButtonAction - * @description - * Register a hardware back button action. Only one action will execute - * when the back button is clicked, so this method decides which of - * the registered back button actions has the highest priority. - * - * For example, if an actionsheet is showing, the back button should - * close the actionsheet, but it should not also go back a page view - * or close a modal which may be open. - * - * The priorities for the existing back button hooks are as follows: - * Return to previous view = 100 - * Close side menu = 150 - * Dismiss modal = 200 - * Close action sheet = 300 - * Dismiss popup = 400 - * Dismiss loading overlay = 500 - * - * Your back button action will override each of the above actions - * whose priority is less than the priority you provide. For example, - * an action assigned a priority of 101 will override the 'return to - * previous view' action, but not any of the other actions. - * - * @param {function} callback Called when the back button is pressed, - * if this listener is the highest priority. - * @param {number} priority Only the highest priority will execute. - * @param {*=} actionId The id to assign this action. Default: a - * random unique id. - * @returns {function} A function that, when called, will deregister - * this backButtonAction. - */ - $backButtonActions: {}, - registerBackButtonAction: function(fn, priority, actionId) { - - if (!self._hasBackButtonHandler) { - // add a back button listener if one hasn't been setup yet - self.$backButtonActions = {}; - self.onHardwareBackButton(self.hardwareBackButtonClick); - self._hasBackButtonHandler = true; - } - - var action = { - id: (actionId ? actionId : ionic.Utils.nextUid()), - priority: (priority ? priority : 0), - fn: fn - }; - self.$backButtonActions[action.id] = action; - - // return a function to de-register this back button action - return function() { - delete self.$backButtonActions[action.id]; - }; - }, - - /** - * @private - */ - hardwareBackButtonClick: function(e) { - // loop through all the registered back button actions - // and only run the last one of the highest priority - var priorityAction, actionId; - for (actionId in self.$backButtonActions) { - if (!priorityAction || self.$backButtonActions[actionId].priority >= priorityAction.priority) { - priorityAction = self.$backButtonActions[actionId]; - } - } - if (priorityAction) { - priorityAction.fn(e); - return priorityAction; - } - }, - - is: function(type) { - return ionic.Platform.is(type); - }, - - /** - * @ngdoc method - * @name $ionicPlatform#on - * @description - * Add Cordova event listeners, such as `pause`, `resume`, `volumedownbutton`, `batterylow`, - * `offline`, etc. More information about available event types can be found in - * [Cordova's event documentation](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events). - * @param {string} type Cordova [event type](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events). - * @param {function} callback Called when the Cordova event is fired. - * @returns {function} Returns a deregistration function to remove the event listener. - */ - on: function(type, cb) { - ionic.Platform.ready(function() { - document.addEventListener(type, cb, false); - }); - return function() { - ionic.Platform.ready(function() { - document.removeEventListener(type, cb); - }); - }; - }, - - /** - * @ngdoc method - * @name $ionicPlatform#ready - * @description - * Trigger a callback once the device is ready, - * or immediately if the device is already ready. - * @param {function=} callback The function to call. - * @returns {promise} A promise which is resolved when the device is ready. - */ - ready: function(cb) { - var q = $q.defer(); - - ionic.Platform.ready(function() { - q.resolve(); - cb && cb(); - }); - - return q.promise; - } - }; - - window.addEventListener('statusTap', function() { - $ionicScrollDelegate.scrollTop(true); - }); - - return self; - }] - }; - -}); - -/** - * @ngdoc service - * @name $ionicPopover - * @module ionic - * @description - * - * Related: {@link ionic.controller:ionicPopover ionicPopover controller}. - * - * The Popover is a view that floats above an app’s content. Popovers provide an - * easy way to present or gather information from the user and are - * commonly used in the following situations: - * - * - Show more info about the current view - * - Select a commonly used tool or configuration - * - Present a list of actions to perform inside one of your views - * - * Put the content of the popover inside of an `` element. - * - * @usage - * ```html - *

- * - *

- * - * - * ``` - * ```js - * angular.module('testApp', ['ionic']) - * .controller('MyController', function($scope, $ionicPopover) { - * - * // .fromTemplate() method - * var template = '

My Popover Title

Hello!
'; - * - * $scope.popover = $ionicPopover.fromTemplate(template, { - * scope: $scope - * }); - * - * // .fromTemplateUrl() method - * $ionicPopover.fromTemplateUrl('my-popover.html', { - * scope: $scope - * }).then(function(popover) { - * $scope.popover = popover; - * }); - * - * - * $scope.openPopover = function($event) { - * $scope.popover.show($event); - * }; - * $scope.closePopover = function() { - * $scope.popover.hide(); - * }; - * //Cleanup the popover when we're done with it! - * $scope.$on('$destroy', function() { - * $scope.popover.remove(); - * }); - * // Execute action on hide popover - * $scope.$on('popover.hidden', function() { - * // Execute action - * }); - * // Execute action on remove popover - * $scope.$on('popover.removed', function() { - * // Execute action - * }); - * }); - * ``` - */ - - -IonicModule -.factory('$ionicPopover', ['$ionicModal', '$ionicPosition', '$document', '$window', -function($ionicModal, $ionicPosition, $document, $window) { - - var POPOVER_BODY_PADDING = 6; - - var POPOVER_OPTIONS = { - viewType: 'popover', - hideDelay: 1, - animation: 'none', - positionView: positionView - }; - - function positionView(target, popoverEle) { - var targetEle = jqLite(target.target || target); - var buttonOffset = $ionicPosition.offset(targetEle); - var popoverWidth = popoverEle.prop('offsetWidth'); - var popoverHeight = popoverEle.prop('offsetHeight'); - // Use innerWidth and innerHeight, because clientWidth and clientHeight - // doesn't work consistently for body on all platforms - var bodyWidth = $window.innerWidth; - var bodyHeight = $window.innerHeight; - - var popoverCSS = { - left: buttonOffset.left + buttonOffset.width / 2 - popoverWidth / 2 - }; - var arrowEle = jqLite(popoverEle[0].querySelector('.popover-arrow')); - - if (popoverCSS.left < POPOVER_BODY_PADDING) { - popoverCSS.left = POPOVER_BODY_PADDING; - } else if (popoverCSS.left + popoverWidth + POPOVER_BODY_PADDING > bodyWidth) { - popoverCSS.left = bodyWidth - popoverWidth - POPOVER_BODY_PADDING; - } - - // If the popover when popped down stretches past bottom of screen, - // make it pop up if there's room above - if (buttonOffset.top + buttonOffset.height + popoverHeight > bodyHeight && - buttonOffset.top - popoverHeight > 0) { - popoverCSS.top = buttonOffset.top - popoverHeight; - popoverEle.addClass('popover-bottom'); - } else { - popoverCSS.top = buttonOffset.top + buttonOffset.height; - popoverEle.removeClass('popover-bottom'); - } - - arrowEle.css({ - left: buttonOffset.left + buttonOffset.width / 2 - - arrowEle.prop('offsetWidth') / 2 - popoverCSS.left + 'px' - }); - - popoverEle.css({ - top: popoverCSS.top + 'px', - left: popoverCSS.left + 'px', - marginLeft: '0', - opacity: '1' - }); - - } - - /** - * @ngdoc controller - * @name ionicPopover - * @module ionic - * @description - * Instantiated by the {@link ionic.service:$ionicPopover} service. - * - * Be sure to call [remove()](#remove) when you are done with each popover - * to clean it up and avoid memory leaks. - * - * Note: a popover will broadcast 'popover.shown', 'popover.hidden', and 'popover.removed' events from its originating - * scope, passing in itself as an event argument. Both the popover.removed and popover.hidden events are - * called when the popover is removed. - */ - - /** - * @ngdoc method - * @name ionicPopover#initialize - * @description Creates a new popover controller instance. - * @param {object} options An options object with the following properties: - * - `{object=}` `scope` The scope to be a child of. - * Default: creates a child of $rootScope. - * - `{boolean=}` `focusFirstInput` Whether to autofocus the first input of - * the popover when shown. Default: false. - * - `{boolean=}` `backdropClickToClose` Whether to close the popover on clicking the backdrop. - * Default: true. - * - `{boolean=}` `hardwareBackButtonClose` Whether the popover can be closed using the hardware - * back button on Android and similar devices. Default: true. - */ - - /** - * @ngdoc method - * @name ionicPopover#show - * @description Show this popover instance. - * @param {$event} $event The $event or target element which the popover should align - * itself next to. - * @returns {promise} A promise which is resolved when the popover is finished animating in. - */ - - /** - * @ngdoc method - * @name ionicPopover#hide - * @description Hide this popover instance. - * @returns {promise} A promise which is resolved when the popover is finished animating out. - */ - - /** - * @ngdoc method - * @name ionicPopover#remove - * @description Remove this popover instance from the DOM and clean up. - * @returns {promise} A promise which is resolved when the popover is finished animating out. - */ - - /** - * @ngdoc method - * @name ionicPopover#isShown - * @returns boolean Whether this popover is currently shown. - */ - - return { - /** - * @ngdoc method - * @name $ionicPopover#fromTemplate - * @param {string} templateString The template string to use as the popovers's - * content. - * @param {object} options Options to be passed to the initialize method. - * @returns {object} An instance of an {@link ionic.controller:ionicPopover} - * controller (ionicPopover is built on top of $ionicPopover). - */ - fromTemplate: function(templateString, options) { - return $ionicModal.fromTemplate(templateString, ionic.Utils.extend({}, POPOVER_OPTIONS, options)); - }, - /** - * @ngdoc method - * @name $ionicPopover#fromTemplateUrl - * @param {string} templateUrl The url to load the template from. - * @param {object} options Options to be passed to the initialize method. - * @returns {promise} A promise that will be resolved with an instance of - * an {@link ionic.controller:ionicPopover} controller (ionicPopover is built on top of $ionicPopover). - */ - fromTemplateUrl: function(url, options) { - return $ionicModal.fromTemplateUrl(url, ionic.Utils.extend({}, POPOVER_OPTIONS, options)); - } - }; - -}]); - - -var POPUP_TPL = - ''; - -/** - * @ngdoc service - * @name $ionicPopup - * @module ionic - * @restrict E - * @codepen zkmhJ - * @description - * - * The Ionic Popup service allows programmatically creating and showing popup - * windows that require the user to respond in order to continue. - * - * The popup system has support for more flexible versions of the built in `alert()`, `prompt()`, - * and `confirm()` functions that users are used to, in addition to allowing popups with completely - * custom content and look. - * - * An input can be given an `autofocus` attribute so it automatically receives focus when - * the popup first shows. However, depending on certain use-cases this can cause issues with - * the tap/click system, which is why Ionic prefers using the `autofocus` attribute as - * an opt-in feature and not the default. - * - * @usage - * A few basic examples, see below for details about all of the options available. - * - * ```js - *angular.module('mySuperApp', ['ionic']) - *.controller('PopupCtrl',function($scope, $ionicPopup, $timeout) { - * - * // Triggered on a button click, or some other target - * $scope.showPopup = function() { - * $scope.data = {}; - * - * // An elaborate, custom popup - * var myPopup = $ionicPopup.show({ - * template: '', - * title: 'Enter Wi-Fi Password', - * subTitle: 'Please use normal things', - * scope: $scope, - * buttons: [ - * { text: 'Cancel' }, - * { - * text: 'Save', - * type: 'button-positive', - * onTap: function(e) { - * if (!$scope.data.wifi) { - * //don't allow the user to close unless he enters wifi password - * e.preventDefault(); - * } else { - * return $scope.data.wifi; - * } - * } - * } - * ] - * }); - * - * myPopup.then(function(res) { - * console.log('Tapped!', res); - * }); - * - * $timeout(function() { - * myPopup.close(); //close the popup after 3 seconds for some reason - * }, 3000); - * }; - * - * // A confirm dialog - * $scope.showConfirm = function() { - * var confirmPopup = $ionicPopup.confirm({ - * title: 'Consume Ice Cream', - * template: 'Are you sure you want to eat this ice cream?' - * }); - * - * confirmPopup.then(function(res) { - * if(res) { - * console.log('You are sure'); - * } else { - * console.log('You are not sure'); - * } - * }); - * }; - * - * // An alert dialog - * $scope.showAlert = function() { - * var alertPopup = $ionicPopup.alert({ - * title: 'Don\'t eat that!', - * template: 'It might taste good' - * }); - * - * alertPopup.then(function(res) { - * console.log('Thank you for not eating my delicious ice cream cone'); - * }); - * }; - *}); - *``` - */ - -IonicModule -.factory('$ionicPopup', [ - '$ionicTemplateLoader', - '$ionicBackdrop', - '$q', - '$timeout', - '$rootScope', - '$ionicBody', - '$compile', - '$ionicPlatform', - '$ionicModal', - 'IONIC_BACK_PRIORITY', -function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $ionicBody, $compile, $ionicPlatform, $ionicModal, IONIC_BACK_PRIORITY) { - //TODO allow this to be configured - var config = { - stackPushDelay: 75 - }; - var popupStack = []; - - var $ionicPopup = { - /** - * @ngdoc method - * @description - * Show a complex popup. This is the master show function for all popups. - * - * A complex popup has a `buttons` array, with each button having a `text` and `type` - * field, in addition to an `onTap` function. The `onTap` function, called when - * the corresponding button on the popup is tapped, will by default close the popup - * and resolve the popup promise with its return value. If you wish to prevent the - * default and keep the popup open on button tap, call `event.preventDefault()` on the - * passed in tap event. Details below. - * - * @name $ionicPopup#show - * @param {object} options The options for the new popup, of the form: - * - * ``` - * { - * title: '', // String. The title of the popup. - * cssClass: '', // String, The custom CSS class name - * subTitle: '', // String (optional). The sub-title of the popup. - * template: '', // String (optional). The html template to place in the popup body. - * templateUrl: '', // String (optional). The URL of an html template to place in the popup body. - * scope: null, // Scope (optional). A scope to link to the popup content. - * buttons: [{ // Array[Object] (optional). Buttons to place in the popup footer. - * text: 'Cancel', - * type: 'button-default', - * onTap: function(e) { - * // e.preventDefault() will stop the popup from closing when tapped. - * e.preventDefault(); - * } - * }, { - * text: 'OK', - * type: 'button-positive', - * onTap: function(e) { - * // Returning a value will cause the promise to resolve with the given value. - * return scope.data.response; - * } - * }] - * } - * ``` - * - * @returns {object} A promise which is resolved when the popup is closed. Has an additional - * `close` function, which can be used to programmatically close the popup. - */ - show: showPopup, - - /** - * @ngdoc method - * @name $ionicPopup#alert - * @description Show a simple alert popup with a message and one button that the user can - * tap to close the popup. - * - * @param {object} options The options for showing the alert, of the form: - * - * ``` - * { - * title: '', // String. The title of the popup. - * cssClass: '', // String, The custom CSS class name - * subTitle: '', // String (optional). The sub-title of the popup. - * template: '', // String (optional). The html template to place in the popup body. - * templateUrl: '', // String (optional). The URL of an html template to place in the popup body. - * okText: '', // String (default: 'OK'). The text of the OK button. - * okType: '', // String (default: 'button-positive'). The type of the OK button. - * } - * ``` - * - * @returns {object} A promise which is resolved when the popup is closed. Has one additional - * function `close`, which can be called with any value to programmatically close the popup - * with the given value. - */ - alert: showAlert, - - /** - * @ngdoc method - * @name $ionicPopup#confirm - * @description - * Show a simple confirm popup with a Cancel and OK button. - * - * Resolves the promise with true if the user presses the OK button, and false if the - * user presses the Cancel button. - * - * @param {object} options The options for showing the confirm popup, of the form: - * - * ``` - * { - * title: '', // String. The title of the popup. - * cssClass: '', // String, The custom CSS class name - * subTitle: '', // String (optional). The sub-title of the popup. - * template: '', // String (optional). The html template to place in the popup body. - * templateUrl: '', // String (optional). The URL of an html template to place in the popup body. - * cancelText: '', // String (default: 'Cancel'). The text of the Cancel button. - * cancelType: '', // String (default: 'button-default'). The type of the Cancel button. - * okText: '', // String (default: 'OK'). The text of the OK button. - * okType: '', // String (default: 'button-positive'). The type of the OK button. - * } - * ``` - * - * @returns {object} A promise which is resolved when the popup is closed. Has one additional - * function `close`, which can be called with any value to programmatically close the popup - * with the given value. - */ - confirm: showConfirm, - - /** - * @ngdoc method - * @name $ionicPopup#prompt - * @description Show a simple prompt popup, which has an input, OK button, and Cancel button. - * Resolves the promise with the value of the input if the user presses OK, and with undefined - * if the user presses Cancel. - * - * ```javascript - * $ionicPopup.prompt({ - * title: 'Password Check', - * template: 'Enter your secret password', - * inputType: 'password', - * inputPlaceholder: 'Your password' - * }).then(function(res) { - * console.log('Your password is', res); - * }); - * ``` - * @param {object} options The options for showing the prompt popup, of the form: - * - * ``` - * { - * title: '', // String. The title of the popup. - * cssClass: '', // String, The custom CSS class name - * subTitle: '', // String (optional). The sub-title of the popup. - * template: '', // String (optional). The html template to place in the popup body. - * templateUrl: '', // String (optional). The URL of an html template to place in the popup body. - * inputType: // String (default: 'text'). The type of input to use - * defaultText: // String (default: ''). The initial value placed into the input. - * maxLength: // Integer (default: null). Specify a maxlength attribute for the input. - * inputPlaceholder: // String (default: ''). A placeholder to use for the input. - * cancelText: // String (default: 'Cancel'. The text of the Cancel button. - * cancelType: // String (default: 'button-default'). The type of the Cancel button. - * okText: // String (default: 'OK'). The text of the OK button. - * okType: // String (default: 'button-positive'). The type of the OK button. - * } - * ``` - * - * @returns {object} A promise which is resolved when the popup is closed. Has one additional - * function `close`, which can be called with any value to programmatically close the popup - * with the given value. - */ - prompt: showPrompt, - /** - * @private for testing - */ - _createPopup: createPopup, - _popupStack: popupStack - }; - - return $ionicPopup; - - function createPopup(options) { - options = extend({ - scope: null, - title: '', - buttons: [] - }, options || {}); - - var self = {}; - self.scope = (options.scope || $rootScope).$new(); - self.element = jqLite(POPUP_TPL); - self.responseDeferred = $q.defer(); - - $ionicBody.get().appendChild(self.element[0]); - $compile(self.element)(self.scope); - - extend(self.scope, { - title: options.title, - buttons: options.buttons, - subTitle: options.subTitle, - cssClass: options.cssClass, - $buttonTapped: function(button, event) { - var result = (button.onTap || noop).apply(self, [event]); - event = event.originalEvent || event; //jquery events - - if (!event.defaultPrevented) { - self.responseDeferred.resolve(result); - } - } - }); - - $q.when( - options.templateUrl ? - $ionicTemplateLoader.load(options.templateUrl) : - (options.template || options.content || '') - ).then(function(template) { - var popupBody = jqLite(self.element[0].querySelector('.popup-body')); - if (template) { - popupBody.html(template); - $compile(popupBody.contents())(self.scope); - } else { - popupBody.remove(); - } - }); - - self.show = function() { - if (self.isShown || self.removed) return; - - $ionicModal.stack.add(self); - self.isShown = true; - ionic.requestAnimationFrame(function() { - //if hidden while waiting for raf, don't show - if (!self.isShown) return; - - self.element.removeClass('popup-hidden'); - self.element.addClass('popup-showing active'); - focusInput(self.element); - }); - }; - - self.hide = function(callback) { - callback = callback || noop; - if (!self.isShown) return callback(); - - $ionicModal.stack.remove(self); - self.isShown = false; - self.element.removeClass('active'); - self.element.addClass('popup-hidden'); - $timeout(callback, 250, false); - }; - - self.remove = function() { - if (self.removed) return; - - self.hide(function() { - self.element.remove(); - self.scope.$destroy(); - }); - - self.removed = true; - }; - - return self; - } - - function onHardwareBackButton() { - var last = popupStack[popupStack.length - 1]; - last && last.responseDeferred.resolve(); - } - - function showPopup(options) { - var popup = $ionicPopup._createPopup(options); - var showDelay = 0; - - if (popupStack.length > 0) { - showDelay = config.stackPushDelay; - $timeout(popupStack[popupStack.length - 1].hide, showDelay, false); - } else { - //Add popup-open & backdrop if this is first popup - $ionicBody.addClass('popup-open'); - $ionicBackdrop.retain(); - //only show the backdrop on the first popup - $ionicPopup._backButtonActionDone = $ionicPlatform.registerBackButtonAction( - onHardwareBackButton, - IONIC_BACK_PRIORITY.popup - ); - } - - // Expose a 'close' method on the returned promise - popup.responseDeferred.promise.close = function popupClose(result) { - if (!popup.removed) popup.responseDeferred.resolve(result); - }; - //DEPRECATED: notify the promise with an object with a close method - popup.responseDeferred.notify({ close: popup.responseDeferred.close }); - - doShow(); - - return popup.responseDeferred.promise; - - function doShow() { - popupStack.push(popup); - $timeout(popup.show, showDelay, false); - - popup.responseDeferred.promise.then(function(result) { - var index = popupStack.indexOf(popup); - if (index !== -1) { - popupStack.splice(index, 1); - } - - popup.remove(); - - if (popupStack.length > 0) { - popupStack[popupStack.length - 1].show(); - } else { - $ionicBackdrop.release(); - //Remove popup-open & backdrop if this is last popup - $timeout(function() { - // wait to remove this due to a 300ms delay native - // click which would trigging whatever was underneath this - if (!popupStack.length) { - $ionicBody.removeClass('popup-open'); - } - }, 400, false); - ($ionicPopup._backButtonActionDone || noop)(); - } - - - return result; - }); - - } - - } - - function focusInput(element) { - var focusOn = element[0].querySelector('[autofocus]'); - if (focusOn) { - focusOn.focus(); - } - } - - function showAlert(opts) { - return showPopup(extend({ - buttons: [{ - text: opts.okText || 'OK', - type: opts.okType || 'button-positive', - onTap: function() { - return true; - } - }] - }, opts || {})); - } - - function showConfirm(opts) { - return showPopup(extend({ - buttons: [{ - text: opts.cancelText || 'Cancel', - type: opts.cancelType || 'button-default', - onTap: function() { return false; } - }, { - text: opts.okText || 'OK', - type: opts.okType || 'button-positive', - onTap: function() { return true; } - }] - }, opts || {})); - } - - function showPrompt(opts) { - var scope = $rootScope.$new(true); - scope.data = {}; - scope.data.fieldtype = opts.inputType ? opts.inputType : 'text'; - scope.data.response = opts.defaultText ? opts.defaultText : ''; - scope.data.placeholder = opts.inputPlaceholder ? opts.inputPlaceholder : ''; - scope.data.maxlength = opts.maxLength ? parseInt(opts.maxLength) : ''; - var text = ''; - if (opts.template && /<[a-z][\s\S]*>/i.test(opts.template) === false) { - text = '' + opts.template + ''; - delete opts.template; - } - return showPopup(extend({ - template: text + '', - scope: scope, - buttons: [{ - text: opts.cancelText || 'Cancel', - type: opts.cancelType || 'button-default', - onTap: function() {} - }, { - text: opts.okText || 'OK', - type: opts.okType || 'button-positive', - onTap: function() { - return scope.data.response || ''; - } - }] - }, opts || {})); - } -}]); - -/** - * @ngdoc service - * @name $ionicPosition - * @module ionic - * @description - * A set of utility methods that can be use to retrieve position of DOM elements. - * It is meant to be used where we need to absolute-position DOM elements in - * relation to other, existing elements (this is the case for tooltips, popovers, etc.). - * - * Adapted from [AngularUI Bootstrap](https://github.com/angular-ui/bootstrap/blob/master/src/position/position.js), - * ([license](https://github.com/angular-ui/bootstrap/blob/master/LICENSE)) - */ -IonicModule -.factory('$ionicPosition', ['$document', '$window', function($document, $window) { - - function getStyle(el, cssprop) { - if (el.currentStyle) { //IE - return el.currentStyle[cssprop]; - } else if ($window.getComputedStyle) { - return $window.getComputedStyle(el)[cssprop]; - } - // finally try and get inline style - return el.style[cssprop]; - } - - /** - * Checks if a given element is statically positioned - * @param element - raw DOM element - */ - function isStaticPositioned(element) { - return (getStyle(element, 'position') || 'static') === 'static'; - } - - /** - * returns the closest, non-statically positioned parentOffset of a given element - * @param element - */ - var parentOffsetEl = function(element) { - var docDomEl = $document[0]; - var offsetParent = element.offsetParent || docDomEl; - while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) { - offsetParent = offsetParent.offsetParent; - } - return offsetParent || docDomEl; - }; - - return { - /** - * @ngdoc method - * @name $ionicPosition#position - * @description Get the current coordinates of the element, relative to the offset parent. - * Read-only equivalent of [jQuery's position function](http://api.jquery.com/position/). - * @param {element} element The element to get the position of. - * @returns {object} Returns an object containing the properties top, left, width and height. - */ - position: function(element) { - var elBCR = this.offset(element); - var offsetParentBCR = { top: 0, left: 0 }; - var offsetParentEl = parentOffsetEl(element[0]); - if (offsetParentEl != $document[0]) { - offsetParentBCR = this.offset(jqLite(offsetParentEl)); - offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; - offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; - } - - var boundingClientRect = element[0].getBoundingClientRect(); - return { - width: boundingClientRect.width || element.prop('offsetWidth'), - height: boundingClientRect.height || element.prop('offsetHeight'), - top: elBCR.top - offsetParentBCR.top, - left: elBCR.left - offsetParentBCR.left - }; - }, - - /** - * @ngdoc method - * @name $ionicPosition#offset - * @description Get the current coordinates of the element, relative to the document. - * Read-only equivalent of [jQuery's offset function](http://api.jquery.com/offset/). - * @param {element} element The element to get the offset of. - * @returns {object} Returns an object containing the properties top, left, width and height. - */ - offset: function(element) { - var boundingClientRect = element[0].getBoundingClientRect(); - return { - width: boundingClientRect.width || element.prop('offsetWidth'), - height: boundingClientRect.height || element.prop('offsetHeight'), - top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), - left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) - }; - } - - }; -}]); - - -/** - * @ngdoc service - * @name $ionicScrollDelegate - * @module ionic - * @description - * Delegate for controlling scrollViews (created by - * {@link ionic.directive:ionContent} and - * {@link ionic.directive:ionScroll} directives). - * - * Methods called directly on the $ionicScrollDelegate service will control all scroll - * views. Use the {@link ionic.service:$ionicScrollDelegate#$getByHandle $getByHandle} - * method to control specific scrollViews. - * - * @usage - * - * ```html - * - * - * - * - * - * ``` - * ```js - * function MainCtrl($scope, $ionicScrollDelegate) { - * $scope.scrollTop = function() { - * $ionicScrollDelegate.scrollTop(); - * }; - * } - * ``` - * - * Example of advanced usage, with two scroll areas using `delegate-handle` - * for fine control. - * - * ```html - * - * - * - * - * - * - * - * - * ``` - * ```js - * function MainCtrl($scope, $ionicScrollDelegate) { - * $scope.scrollMainToTop = function() { - * $ionicScrollDelegate.$getByHandle('mainScroll').scrollTop(); - * }; - * $scope.scrollSmallToTop = function() { - * $ionicScrollDelegate.$getByHandle('small').scrollTop(); - * }; - * } - * ``` - */ -IonicModule -.service('$ionicScrollDelegate', ionic.DelegateService([ - /** - * @ngdoc method - * @name $ionicScrollDelegate#resize - * @description Tell the scrollView to recalculate the size of its container. - */ - 'resize', - /** - * @ngdoc method - * @name $ionicScrollDelegate#scrollTop - * @param {boolean=} shouldAnimate Whether the scroll should animate. - */ - 'scrollTop', - /** - * @ngdoc method - * @name $ionicScrollDelegate#scrollBottom - * @param {boolean=} shouldAnimate Whether the scroll should animate. - */ - 'scrollBottom', - /** - * @ngdoc method - * @name $ionicScrollDelegate#scrollTo - * @param {number} left The x-value to scroll to. - * @param {number} top The y-value to scroll to. - * @param {boolean=} shouldAnimate Whether the scroll should animate. - */ - 'scrollTo', - /** - * @ngdoc method - * @name $ionicScrollDelegate#scrollBy - * @param {number} left The x-offset to scroll by. - * @param {number} top The y-offset to scroll by. - * @param {boolean=} shouldAnimate Whether the scroll should animate. - */ - 'scrollBy', - /** - * @ngdoc method - * @name $ionicScrollDelegate#zoomTo - * @param {number} level Level to zoom to. - * @param {boolean=} animate Whether to animate the zoom. - * @param {number=} originLeft Zoom in at given left coordinate. - * @param {number=} originTop Zoom in at given top coordinate. - */ - 'zoomTo', - /** - * @ngdoc method - * @name $ionicScrollDelegate#zoomBy - * @param {number} factor The factor to zoom by. - * @param {boolean=} animate Whether to animate the zoom. - * @param {number=} originLeft Zoom in at given left coordinate. - * @param {number=} originTop Zoom in at given top coordinate. - */ - 'zoomBy', - /** - * @ngdoc method - * @name $ionicScrollDelegate#getScrollPosition - * @returns {object} The scroll position of this view, with the following properties: - * - `{number}` `left` The distance the user has scrolled from the left (starts at 0). - * - `{number}` `top` The distance the user has scrolled from the top (starts at 0). - */ - 'getScrollPosition', - /** - * @ngdoc method - * @name $ionicScrollDelegate#anchorScroll - * @description Tell the scrollView to scroll to the element with an id - * matching window.location.hash. - * - * If no matching element is found, it will scroll to top. - * - * @param {boolean=} shouldAnimate Whether the scroll should animate. - */ - 'anchorScroll', - /** - * @ngdoc method - * @name $ionicScrollDelegate#freezeScroll - * @description Does not allow this scroll view to scroll either x or y. - * @param {boolean=} shouldFreeze Should this scroll view be prevented from scrolling or not. - * @returns {boolean} If the scroll view is being prevented from scrolling or not. - */ - 'freezeScroll', - /** - * @ngdoc method - * @name $ionicScrollDelegate#freezeAllScrolls - * @description Does not allow any of the app's scroll views to scroll either x or y. - * @param {boolean=} shouldFreeze Should all app scrolls be prevented from scrolling or not. - */ - 'freezeAllScrolls', - /** - * @ngdoc method - * @name $ionicScrollDelegate#getScrollView - * @returns {object} The scrollView associated with this delegate. - */ - 'getScrollView' - /** - * @ngdoc method - * @name $ionicScrollDelegate#$getByHandle - * @param {string} handle - * @returns `delegateInstance` A delegate instance that controls only the - * scrollViews with `delegate-handle` matching the given handle. - * - * Example: `$ionicScrollDelegate.$getByHandle('my-handle').scrollTop();` - */ -])); - - -/** - * @ngdoc service - * @name $ionicSideMenuDelegate - * @module ionic - * - * @description - * Delegate for controlling the {@link ionic.directive:ionSideMenus} directive. - * - * Methods called directly on the $ionicSideMenuDelegate service will control all side - * menus. Use the {@link ionic.service:$ionicSideMenuDelegate#$getByHandle $getByHandle} - * method to control specific ionSideMenus instances. - * - * @usage - * - * ```html - * - * - * - * Content! - * - * - * - * Left Menu! - * - * - * - * ``` - * ```js - * function MainCtrl($scope, $ionicSideMenuDelegate) { - * $scope.toggleLeftSideMenu = function() { - * $ionicSideMenuDelegate.toggleLeft(); - * }; - * } - * ``` - */ -IonicModule -.service('$ionicSideMenuDelegate', ionic.DelegateService([ - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#toggleLeft - * @description Toggle the left side menu (if it exists). - * @param {boolean=} isOpen Whether to open or close the menu. - * Default: Toggles the menu. - */ - 'toggleLeft', - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#toggleRight - * @description Toggle the right side menu (if it exists). - * @param {boolean=} isOpen Whether to open or close the menu. - * Default: Toggles the menu. - */ - 'toggleRight', - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#getOpenRatio - * @description Gets the ratio of open amount over menu width. For example, a - * menu of width 100 that is opened by 50 pixels is 50% opened, and would return - * a ratio of 0.5. - * - * @returns {float} 0 if nothing is open, between 0 and 1 if left menu is - * opened/opening, and between 0 and -1 if right menu is opened/opening. - */ - 'getOpenRatio', - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#isOpen - * @returns {boolean} Whether either the left or right menu is currently opened. - */ - 'isOpen', - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#isOpenLeft - * @returns {boolean} Whether the left menu is currently opened. - */ - 'isOpenLeft', - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#isOpenRight - * @returns {boolean} Whether the right menu is currently opened. - */ - 'isOpenRight', - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#canDragContent - * @param {boolean=} canDrag Set whether the content can or cannot be dragged to open - * side menus. - * @returns {boolean} Whether the content can be dragged to open side menus. - */ - 'canDragContent', - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#edgeDragThreshold - * @param {boolean|number=} value Set whether the content drag can only start if it is below a certain threshold distance from the edge of the screen. Accepts three different values: - * - If a non-zero number is given, that many pixels is used as the maximum allowed distance from the edge that starts dragging the side menu. - * - If true is given, the default number of pixels (25) is used as the maximum allowed distance. - * - If false or 0 is given, the edge drag threshold is disabled, and dragging from anywhere on the content is allowed. - * @returns {boolean} Whether the drag can start only from within the edge of screen threshold. - */ - 'edgeDragThreshold' - /** - * @ngdoc method - * @name $ionicSideMenuDelegate#$getByHandle - * @param {string} handle - * @returns `delegateInstance` A delegate instance that controls only the - * {@link ionic.directive:ionSideMenus} directives with `delegate-handle` matching - * the given handle. - * - * Example: `$ionicSideMenuDelegate.$getByHandle('my-handle').toggleLeft();` - */ -])); - - -/** - * @ngdoc service - * @name $ionicSlideBoxDelegate - * @module ionic - * @description - * Delegate that controls the {@link ionic.directive:ionSlideBox} directive. - * - * Methods called directly on the $ionicSlideBoxDelegate service will control all slide boxes. Use the {@link ionic.service:$ionicSlideBoxDelegate#$getByHandle $getByHandle} - * method to control specific slide box instances. - * - * @usage - * - * ```html - * - * - * - *
- * - *
- *
- * - *
- * Slide 2! - *
- *
- *
- *
- * ``` - * ```js - * function MyCtrl($scope, $ionicSlideBoxDelegate) { - * $scope.nextSlide = function() { - * $ionicSlideBoxDelegate.next(); - * } - * } - * ``` - */ -IonicModule -.service('$ionicSlideBoxDelegate', ionic.DelegateService([ - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#update - * @description - * Update the slidebox (for example if using Angular with ng-repeat, - * resize it for the elements inside). - */ - 'update', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#slide - * @param {number} to The index to slide to. - * @param {number=} speed The number of milliseconds the change should take. - */ - 'slide', - 'select', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#enableSlide - * @param {boolean=} shouldEnable Whether to enable sliding the slidebox. - * @returns {boolean} Whether sliding is enabled. - */ - 'enableSlide', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#previous - * @param {number=} speed The number of milliseconds the change should take. - * @description Go to the previous slide. Wraps around if at the beginning. - */ - 'previous', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#next - * @param {number=} speed The number of milliseconds the change should take. - * @description Go to the next slide. Wraps around if at the end. - */ - 'next', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#stop - * @description Stop sliding. The slideBox will not move again until - * explicitly told to do so. - */ - 'stop', - 'autoPlay', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#start - * @description Start sliding again if the slideBox was stopped. - */ - 'start', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#currentIndex - * @returns number The index of the current slide. - */ - 'currentIndex', - 'selected', - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#slidesCount - * @returns number The number of slides there are currently. - */ - 'slidesCount', - 'count', - 'loop' - /** - * @ngdoc method - * @name $ionicSlideBoxDelegate#$getByHandle - * @param {string} handle - * @returns `delegateInstance` A delegate instance that controls only the - * {@link ionic.directive:ionSlideBox} directives with `delegate-handle` matching - * the given handle. - * - * Example: `$ionicSlideBoxDelegate.$getByHandle('my-handle').stop();` - */ -])); - - -/** - * @ngdoc service - * @name $ionicTabsDelegate - * @module ionic - * - * @description - * Delegate for controlling the {@link ionic.directive:ionTabs} directive. - * - * Methods called directly on the $ionicTabsDelegate service will control all ionTabs - * directives. Use the {@link ionic.service:$ionicTabsDelegate#$getByHandle $getByHandle} - * method to control specific ionTabs instances. - * - * @usage - * - * ```html - * - * - * - * - * Hello tab 1! - * - * - * Hello tab 2! - * - * - * - * ``` - * ```js - * function MyCtrl($scope, $ionicTabsDelegate) { - * $scope.selectTabWithIndex = function(index) { - * $ionicTabsDelegate.select(index); - * } - * } - * ``` - */ -IonicModule -.service('$ionicTabsDelegate', ionic.DelegateService([ - /** - * @ngdoc method - * @name $ionicTabsDelegate#select - * @description Select the tab matching the given index. - * - * @param {number} index Index of the tab to select. - */ - 'select', - /** - * @ngdoc method - * @name $ionicTabsDelegate#selectedIndex - * @returns `number` The index of the selected tab, or -1. - */ - 'selectedIndex', - /** - * @ngdoc method - * @name $ionicTabsDelegate#showBar - * @description - * Set/get whether the {@link ionic.directive:ionTabs} is shown - * @param {boolean} show Whether to show the bar. - * @returns {boolean} Whether the bar is shown. - */ - 'showBar' - /** - * @ngdoc method - * @name $ionicTabsDelegate#$getByHandle - * @param {string} handle - * @returns `delegateInstance` A delegate instance that controls only the - * {@link ionic.directive:ionTabs} directives with `delegate-handle` matching - * the given handle. - * - * Example: `$ionicTabsDelegate.$getByHandle('my-handle').select(0);` - */ -])); - -// closure to keep things neat -(function() { - var templatesToCache = []; - -/** - * @ngdoc service - * @name $ionicTemplateCache - * @module ionic - * @description A service that preemptively caches template files to eliminate transition flicker and boost performance. - * @usage - * State templates are cached automatically, but you can optionally cache other templates. - * - * ```js - * $ionicTemplateCache('myNgIncludeTemplate.html'); - * ``` - * - * Optionally disable all preemptive caching with the `$ionicConfigProvider` or individual states by setting `prefetchTemplate` - * in the `$state` definition - * - * ```js - * angular.module('myApp', ['ionic']) - * .config(function($stateProvider, $ionicConfigProvider) { - * - * // disable preemptive template caching globally - * $ionicConfigProvider.templates.prefetch(false); - * - * // disable individual states - * $stateProvider - * .state('tabs', { - * url: "/tab", - * abstract: true, - * prefetchTemplate: false, - * templateUrl: "tabs-templates/tabs.html" - * }) - * .state('tabs.home', { - * url: "/home", - * views: { - * 'home-tab': { - * prefetchTemplate: false, - * templateUrl: "tabs-templates/home.html", - * controller: 'HomeTabCtrl' - * } - * } - * }); - * }); - * ``` - */ -IonicModule -.factory('$ionicTemplateCache', [ -'$http', -'$templateCache', -'$timeout', -function($http, $templateCache, $timeout) { - var toCache = templatesToCache, - hasRun; - - function $ionicTemplateCache(templates) { - if (typeof templates === 'undefined') { - return run(); - } - if (isString(templates)) { - templates = [templates]; - } - forEach(templates, function(template) { - toCache.push(template); - }); - if (hasRun) { - run(); - } - } - - // run through methods - internal method - function run() { - var template; - $ionicTemplateCache._runCount++; - - hasRun = true; - // ignore if race condition already zeroed out array - if (toCache.length === 0) return; - - var i = 0; - while (i < 4 && (template = toCache.pop())) { - // note that inline templates are ignored by this request - if (isString(template)) $http.get(template, { cache: $templateCache }); - i++; - } - // only preload 3 templates a second - if (toCache.length) { - $timeout(run, 1000); - } - } - - // exposing for testing - $ionicTemplateCache._runCount = 0; - // default method - return $ionicTemplateCache; -}]) - -// Intercepts the $stateprovider.state() command to look for templateUrls that can be cached -.config([ -'$stateProvider', -'$ionicConfigProvider', -function($stateProvider, $ionicConfigProvider) { - var stateProviderState = $stateProvider.state; - $stateProvider.state = function(stateName, definition) { - // don't even bother if it's disabled. note, another config may run after this, so it's not a catch-all - if (typeof definition === 'object') { - var enabled = definition.prefetchTemplate !== false && templatesToCache.length < $ionicConfigProvider.templates.maxPrefetch(); - if (enabled && isString(definition.templateUrl)) templatesToCache.push(definition.templateUrl); - if (angular.isObject(definition.views)) { - for (var key in definition.views) { - enabled = definition.views[key].prefetchTemplate !== false && templatesToCache.length < $ionicConfigProvider.templates.maxPrefetch(); - if (enabled && isString(definition.views[key].templateUrl)) templatesToCache.push(definition.views[key].templateUrl); - } - } - } - return stateProviderState.call($stateProvider, stateName, definition); - }; -}]) - -// process the templateUrls collected by the $stateProvider, adding them to the cache -.run(['$ionicTemplateCache', function($ionicTemplateCache) { - $ionicTemplateCache(); -}]); - -})(); - -IonicModule -.factory('$ionicTemplateLoader', [ - '$compile', - '$controller', - '$http', - '$q', - '$rootScope', - '$templateCache', -function($compile, $controller, $http, $q, $rootScope, $templateCache) { - - return { - load: fetchTemplate, - compile: loadAndCompile - }; - - function fetchTemplate(url) { - return $http.get(url, {cache: $templateCache}) - .then(function(response) { - return response.data && response.data.trim(); - }); - } - - function loadAndCompile(options) { - options = extend({ - template: '', - templateUrl: '', - scope: null, - controller: null, - locals: {}, - appendTo: null - }, options || {}); - - var templatePromise = options.templateUrl ? - this.load(options.templateUrl) : - $q.when(options.template); - - return templatePromise.then(function(template) { - var controller; - var scope = options.scope || $rootScope.$new(); - - //Incase template doesn't have just one root element, do this - var element = jqLite('
').html(template).contents(); - - if (options.controller) { - controller = $controller( - options.controller, - extend(options.locals, { - $scope: scope - }) - ); - element.children().data('$ngControllerController', controller); - } - if (options.appendTo) { - jqLite(options.appendTo).append(element); - } - - $compile(element)(scope); - - return { - element: element, - scope: scope - }; - }); - } - -}]); - -/** - * @private - * DEPRECATED, as of v1.0.0-beta14 ------- - */ -IonicModule -.factory('$ionicViewService', ['$ionicHistory', '$log', function($ionicHistory, $log) { - - function warn(oldMethod, newMethod) { - $log.warn('$ionicViewService' + oldMethod + ' is deprecated, please use $ionicHistory' + newMethod + ' instead: http://ionicframework.com/docs/nightly/api/service/$ionicHistory/'); - } - - warn('', ''); - - var methodsMap = { - getCurrentView: 'currentView', - getBackView: 'backView', - getForwardView: 'forwardView', - getCurrentStateName: 'currentStateName', - nextViewOptions: 'nextViewOptions', - clearHistory: 'clearHistory' - }; - - forEach(methodsMap, function(newMethod, oldMethod) { - methodsMap[oldMethod] = function() { - warn('.' + oldMethod, '.' + newMethod); - return $ionicHistory[newMethod].apply(this, arguments); - }; - }); - - return methodsMap; - -}]); - -/** - * @private - * TODO document - */ - -IonicModule.factory('$ionicViewSwitcher', [ - '$timeout', - '$document', - '$q', - '$ionicClickBlock', - '$ionicConfig', - '$ionicNavBarDelegate', -function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDelegate) { - - var TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; - var DATA_NO_CACHE = '$noCache'; - var DATA_DESTROY_ELE = '$destroyEle'; - var DATA_ELE_IDENTIFIER = '$eleId'; - var DATA_VIEW_ACCESSED = '$accessed'; - var DATA_FALLBACK_TIMER = '$fallbackTimer'; - var DATA_VIEW = '$viewData'; - var NAV_VIEW_ATTR = 'nav-view'; - var VIEW_STATUS_ACTIVE = 'active'; - var VIEW_STATUS_CACHED = 'cached'; - var VIEW_STATUS_STAGED = 'stage'; - - var transitionCounter = 0; - var nextTransition, nextDirection; - ionic.transition = ionic.transition || {}; - ionic.transition.isActive = false; - var isActiveTimer; - var cachedAttr = ionic.DomUtil.cachedAttr; - var transitionPromises = []; - var defaultTimeout = 1100; - - var ionicViewSwitcher = { - - create: function(navViewCtrl, viewLocals, enteringView, leavingView, renderStart, renderEnd) { - // get a reference to an entering/leaving element if they exist - // loop through to see if the view is already in the navViewElement - var enteringEle, leavingEle; - var transitionId = ++transitionCounter; - var alreadyInDom; - - var switcher = { - - init: function(registerData, callback) { - ionicViewSwitcher.isTransitioning(true); - - switcher.loadViewElements(registerData); - - switcher.render(registerData, function() { - callback && callback(); - }); - }, - - loadViewElements: function(registerData) { - var x, l, viewEle; - var viewElements = navViewCtrl.getViewElements(); - var enteringEleIdentifier = getViewElementIdentifier(viewLocals, enteringView); - var navViewActiveEleId = navViewCtrl.activeEleId(); - - for (x = 0, l = viewElements.length; x < l; x++) { - viewEle = viewElements.eq(x); - - if (viewEle.data(DATA_ELE_IDENTIFIER) === enteringEleIdentifier) { - // we found an existing element in the DOM that should be entering the view - if (viewEle.data(DATA_NO_CACHE)) { - // the existing element should not be cached, don't use it - viewEle.data(DATA_ELE_IDENTIFIER, enteringEleIdentifier + ionic.Utils.nextUid()); - viewEle.data(DATA_DESTROY_ELE, true); - - } else { - enteringEle = viewEle; - } - - } else if (isDefined(navViewActiveEleId) && viewEle.data(DATA_ELE_IDENTIFIER) === navViewActiveEleId) { - leavingEle = viewEle; - } - - if (enteringEle && leavingEle) break; - } - - alreadyInDom = !!enteringEle; - - if (!alreadyInDom) { - // still no existing element to use - // create it using existing template/scope/locals - enteringEle = registerData.ele || ionicViewSwitcher.createViewEle(viewLocals); - - // existing elements in the DOM are looked up by their state name and state id - enteringEle.data(DATA_ELE_IDENTIFIER, enteringEleIdentifier); - } - - if (renderEnd) { - navViewCtrl.activeEleId(enteringEleIdentifier); - } - - registerData.ele = null; - }, - - render: function(registerData, callback) { - if (alreadyInDom) { - // it was already found in the DOM, just reconnect the scope - ionic.Utils.reconnectScope(enteringEle.scope()); - - } else { - // the entering element is not already in the DOM - // set that the entering element should be "staged" and its - // styles of where this element will go before it hits the DOM - navViewAttr(enteringEle, VIEW_STATUS_STAGED); - - var enteringData = getTransitionData(viewLocals, enteringEle, registerData.direction, enteringView); - var transitionFn = $ionicConfig.transitions.views[enteringData.transition] || $ionicConfig.transitions.views.none; - transitionFn(enteringEle, null, enteringData.direction, true).run(0); - - enteringEle.data(DATA_VIEW, { - viewId: enteringData.viewId, - historyId: enteringData.historyId, - stateName: enteringData.stateName, - stateParams: enteringData.stateParams - }); - - // if the current state has cache:false - // or the element has cache-view="false" attribute - if (viewState(viewLocals).cache === false || viewState(viewLocals).cache === 'false' || - enteringEle.attr('cache-view') == 'false' || $ionicConfig.views.maxCache() === 0) { - enteringEle.data(DATA_NO_CACHE, true); - } - - // append the entering element to the DOM, create a new scope and run link - var viewScope = navViewCtrl.appendViewElement(enteringEle, viewLocals); - - delete enteringData.direction; - delete enteringData.transition; - viewScope.$emit('$ionicView.loaded', enteringData); - } - - // update that this view was just accessed - enteringEle.data(DATA_VIEW_ACCESSED, Date.now()); - - callback && callback(); - }, - - transition: function(direction, enableBack, allowAnimate) { - var deferred; - var enteringData = getTransitionData(viewLocals, enteringEle, direction, enteringView); - var leavingData = extend(extend({}, enteringData), getViewData(leavingView)); - enteringData.transitionId = leavingData.transitionId = transitionId; - enteringData.fromCache = !!alreadyInDom; - enteringData.enableBack = !!enableBack; - enteringData.renderStart = renderStart; - enteringData.renderEnd = renderEnd; - - cachedAttr(enteringEle.parent(), 'nav-view-transition', enteringData.transition); - cachedAttr(enteringEle.parent(), 'nav-view-direction', enteringData.direction); - - // cancel any previous transition complete fallbacks - $timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER)); - - // get the transition ready and see if it'll animate - var transitionFn = $ionicConfig.transitions.views[enteringData.transition] || $ionicConfig.transitions.views.none; - var viewTransition = transitionFn(enteringEle, leavingEle, enteringData.direction, - enteringData.shouldAnimate && allowAnimate && renderEnd); - - if (viewTransition.shouldAnimate) { - // attach transitionend events (and fallback timer) - enteringEle.on(TRANSITIONEND_EVENT, completeOnTransitionEnd); - enteringEle.data(DATA_FALLBACK_TIMER, $timeout(transitionComplete, defaultTimeout)); - $ionicClickBlock.show(defaultTimeout); - } - - if (renderStart) { - // notify the views "before" the transition starts - switcher.emit('before', enteringData, leavingData); - - // stage entering element, opacity 0, no transition duration - navViewAttr(enteringEle, VIEW_STATUS_STAGED); - - // render the elements in the correct location for their starting point - viewTransition.run(0); - } - - if (renderEnd) { - // create a promise so we can keep track of when all transitions finish - // only required if this transition should complete - deferred = $q.defer(); - transitionPromises.push(deferred.promise); - } - - if (renderStart && renderEnd) { - // CSS "auto" transitioned, not manually transitioned - // wait a frame so the styles apply before auto transitioning - $timeout(function() { - ionic.requestAnimationFrame(onReflow); - }); - } else if (!renderEnd) { - // just the start of a manual transition - // but it will not render the end of the transition - navViewAttr(enteringEle, 'entering'); - navViewAttr(leavingEle, 'leaving'); - - // return the transition run method so each step can be ran manually - return { - run: viewTransition.run, - cancel: function(shouldAnimate) { - if (shouldAnimate) { - enteringEle.on(TRANSITIONEND_EVENT, cancelOnTransitionEnd); - enteringEle.data(DATA_FALLBACK_TIMER, $timeout(cancelTransition, defaultTimeout)); - $ionicClickBlock.show(defaultTimeout); - } else { - cancelTransition(); - } - viewTransition.shouldAnimate = shouldAnimate; - viewTransition.run(0); - viewTransition = null; - } - }; - - } else if (renderEnd) { - // just the end of a manual transition - // happens after the manual transition has completed - // and a full history change has happened - onReflow(); - } - - - function onReflow() { - // remove that we're staging the entering element so it can auto transition - navViewAttr(enteringEle, viewTransition.shouldAnimate ? 'entering' : VIEW_STATUS_ACTIVE); - navViewAttr(leavingEle, viewTransition.shouldAnimate ? 'leaving' : VIEW_STATUS_CACHED); - - // start the auto transition and let the CSS take over - viewTransition.run(1); - - // trigger auto transitions on the associated nav bars - $ionicNavBarDelegate._instances.forEach(function(instance) { - instance.triggerTransitionStart(transitionId); - }); - - if (!viewTransition.shouldAnimate) { - // no animated auto transition - transitionComplete(); - } - } - - // Make sure that transitionend events bubbling up from children won't fire - // transitionComplete. Will only go forward if ev.target == the element listening. - function completeOnTransitionEnd(ev) { - if (ev.target !== this) return; - transitionComplete(); - } - function transitionComplete() { - if (transitionComplete.x) return; - transitionComplete.x = true; - - enteringEle.off(TRANSITIONEND_EVENT, completeOnTransitionEnd); - $timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER)); - leavingEle && $timeout.cancel(leavingEle.data(DATA_FALLBACK_TIMER)); - - // resolve that this one transition (there could be many w/ nested views) - deferred && deferred.resolve(navViewCtrl); - - // the most recent transition added has completed and all the active - // transition promises should be added to the services array of promises - if (transitionId === transitionCounter) { - $q.all(transitionPromises).then(ionicViewSwitcher.transitionEnd); - - // emit that the views have finished transitioning - // each parent nav-view will update which views are active and cached - switcher.emit('after', enteringData, leavingData); - switcher.cleanup(enteringData); - } - - // tell the nav bars that the transition has ended - $ionicNavBarDelegate._instances.forEach(function(instance) { - instance.triggerTransitionEnd(); - }); - - - // remove any references that could cause memory issues - nextTransition = nextDirection = enteringView = leavingView = enteringEle = leavingEle = null; - } - - // Make sure that transitionend events bubbling up from children won't fire - // transitionComplete. Will only go forward if ev.target == the element listening. - function cancelOnTransitionEnd(ev) { - if (ev.target !== this) return; - cancelTransition(); - } - function cancelTransition() { - navViewAttr(enteringEle, VIEW_STATUS_CACHED); - navViewAttr(leavingEle, VIEW_STATUS_ACTIVE); - enteringEle.off(TRANSITIONEND_EVENT, cancelOnTransitionEnd); - $timeout.cancel(enteringEle.data(DATA_FALLBACK_TIMER)); - ionicViewSwitcher.transitionEnd([navViewCtrl]); - } - - }, - - emit: function(step, enteringData, leavingData) { - var enteringScope = enteringEle.scope(), - leavingScope = leavingEle && leavingEle.scope(); - - if (step == 'after') { - if (enteringScope) { - enteringScope.$emit('$ionicView.enter', enteringData); - } - - if (leavingScope) { - leavingScope.$emit('$ionicView.leave', leavingData); - - } else if (enteringScope && leavingData && leavingData.viewId) { - enteringScope.$emit('$ionicNavView.leave', leavingData); - } - } - - if (enteringScope) { - enteringScope.$emit('$ionicView.' + step + 'Enter', enteringData); - } - - if (leavingScope) { - leavingScope.$emit('$ionicView.' + step + 'Leave', leavingData); - - } else if (enteringScope && leavingData && leavingData.viewId) { - enteringScope.$emit('$ionicNavView.' + step + 'Leave', leavingData); - } - }, - - cleanup: function(transData) { - // check if any views should be removed - if (leavingEle && transData.direction == 'back' && !$ionicConfig.views.forwardCache()) { - // if they just navigated back we can destroy the forward view - // do not remove forward views if cacheForwardViews config is true - destroyViewEle(leavingEle); - } - - var viewElements = navViewCtrl.getViewElements(); - var viewElementsLength = viewElements.length; - var x, viewElement; - var removeOldestAccess = (viewElementsLength - 1) > $ionicConfig.views.maxCache(); - var removableEle; - var oldestAccess = Date.now(); - - for (x = 0; x < viewElementsLength; x++) { - viewElement = viewElements.eq(x); - - if (removeOldestAccess && viewElement.data(DATA_VIEW_ACCESSED) < oldestAccess) { - // remember what was the oldest element to be accessed so it can be destroyed - oldestAccess = viewElement.data(DATA_VIEW_ACCESSED); - removableEle = viewElements.eq(x); - - } else if (viewElement.data(DATA_DESTROY_ELE) && navViewAttr(viewElement) != VIEW_STATUS_ACTIVE) { - destroyViewEle(viewElement); - } - } - - destroyViewEle(removableEle); - - if (enteringEle.data(DATA_NO_CACHE)) { - enteringEle.data(DATA_DESTROY_ELE, true); - } - }, - - enteringEle: function() { return enteringEle; }, - leavingEle: function() { return leavingEle; } - - }; - - return switcher; - }, - - transitionEnd: function(navViewCtrls) { - forEach(navViewCtrls, function(navViewCtrl) { - navViewCtrl.transitionEnd(); - }); - - ionicViewSwitcher.isTransitioning(false); - $ionicClickBlock.hide(); - transitionPromises = []; - }, - - nextTransition: function(val) { - nextTransition = val; - }, - - nextDirection: function(val) { - nextDirection = val; - }, - - isTransitioning: function(val) { - if (arguments.length) { - ionic.transition.isActive = !!val; - $timeout.cancel(isActiveTimer); - if (val) { - isActiveTimer = $timeout(function() { - ionicViewSwitcher.isTransitioning(false); - }, 999); - } - } - return ionic.transition.isActive; - }, - - createViewEle: function(viewLocals) { - var containerEle = $document[0].createElement('div'); - if (viewLocals && viewLocals.$template) { - containerEle.innerHTML = viewLocals.$template; - if (containerEle.children.length === 1) { - containerEle.children[0].classList.add('pane'); - return jqLite(containerEle.children[0]); - } - } - containerEle.className = "pane"; - return jqLite(containerEle); - }, - - viewEleIsActive: function(viewEle, isActiveAttr) { - navViewAttr(viewEle, isActiveAttr ? VIEW_STATUS_ACTIVE : VIEW_STATUS_CACHED); - }, - - getTransitionData: getTransitionData, - navViewAttr: navViewAttr, - destroyViewEle: destroyViewEle - - }; - - return ionicViewSwitcher; - - - function getViewElementIdentifier(locals, view) { - if (viewState(locals)['abstract']) return viewState(locals).name; - if (view) return view.stateId || view.viewId; - return ionic.Utils.nextUid(); - } - - function viewState(locals) { - return locals && locals.$$state && locals.$$state.self || {}; - } - - function getTransitionData(viewLocals, enteringEle, direction, view) { - // Priority - // 1) attribute directive on the button/link to this view - // 2) entering element's attribute - // 3) entering view's $state config property - // 4) view registration data - // 5) global config - // 6) fallback value - - var state = viewState(viewLocals); - var viewTransition = nextTransition || cachedAttr(enteringEle, 'view-transition') || state.viewTransition || $ionicConfig.views.transition() || 'ios'; - var navBarTransition = $ionicConfig.navBar.transition(); - direction = nextDirection || cachedAttr(enteringEle, 'view-direction') || state.viewDirection || direction || 'none'; - - return extend(getViewData(view), { - transition: viewTransition, - navBarTransition: navBarTransition === 'view' ? viewTransition : navBarTransition, - direction: direction, - shouldAnimate: (viewTransition !== 'none' && direction !== 'none') - }); - } - - function getViewData(view) { - view = view || {}; - return { - viewId: view.viewId, - historyId: view.historyId, - stateId: view.stateId, - stateName: view.stateName, - stateParams: view.stateParams - }; - } - - function navViewAttr(ele, value) { - if (arguments.length > 1) { - cachedAttr(ele, NAV_VIEW_ATTR, value); - } else { - return cachedAttr(ele, NAV_VIEW_ATTR); - } - } - - function destroyViewEle(ele) { - // we found an element that should be removed - // destroy its scope, then remove the element - if (ele && ele.length) { - var viewScope = ele.scope(); - if (viewScope) { - viewScope.$emit('$ionicView.unloaded', ele.data(DATA_VIEW)); - viewScope.$destroy(); - } - ele.remove(); - } - } - -}]); - -/** - * ================== angular-ios9-uiwebview.patch.js v1.1.1 ================== - * - * This patch works around iOS9 UIWebView regression that causes infinite digest - * errors in Angular. - * - * The patch can be applied to Angular 1.2.0 – 1.4.5. Newer versions of Angular - * have the workaround baked in. - * - * To apply this patch load/bundle this file with your application and add a - * dependency on the "ngIOS9UIWebViewPatch" module to your main app module. - * - * For example: - * - * ``` - * angular.module('myApp', ['ngRoute'])` - * ``` - * - * becomes - * - * ``` - * angular.module('myApp', ['ngRoute', 'ngIOS9UIWebViewPatch']) - * ``` - * - * - * More info: - * - https://openradar.appspot.com/22186109 - * - https://github.com/angular/angular.js/issues/12241 - * - https://github.com/driftyco/ionic/issues/4082 - * - * - * @license AngularJS - * (c) 2010-2015 Google, Inc. http://angularjs.org - * License: MIT - */ - -angular.module('ngIOS9UIWebViewPatch', ['ng']).config(['$provide', function($provide) { - 'use strict'; - - $provide.decorator('$browser', ['$delegate', '$window', function($delegate, $window) { - - if (isIOS9UIWebView($window.navigator.userAgent)) { - return applyIOS9Shim($delegate); - } - - return $delegate; - - function isIOS9UIWebView(userAgent) { - return /(iPhone|iPad|iPod).* OS 9_\d/.test(userAgent) && !/Version\/9\./.test(userAgent); - } - - function applyIOS9Shim(browser) { - var pendingLocationUrl = null; - var originalUrlFn = browser.url; - - browser.url = function() { - if (arguments.length) { - pendingLocationUrl = arguments[0]; - return originalUrlFn.apply(browser, arguments); - } - - return pendingLocationUrl || originalUrlFn.apply(browser, arguments); - }; - - window.addEventListener('popstate', clearPendingLocationUrl, false); - window.addEventListener('hashchange', clearPendingLocationUrl, false); - - function clearPendingLocationUrl() { - pendingLocationUrl = null; - } - - return browser; - } - }]); -}]); - -/** - * @private - * Parts of Ionic requires that $scope data is attached to the element. - * We do not want to disable adding $scope data to the $element when - * $compileProvider.debugInfoEnabled(false) is used. - */ -IonicModule.config(['$provide', function($provide) { - $provide.decorator('$compile', ['$delegate', function($compile) { - $compile.$$addScopeInfo = function $$addScopeInfo($element, scope, isolated, noTemplate) { - var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope'; - $element.data(dataName, scope); - }; - return $compile; - }]); -}]); - -/** - * @private - */ -IonicModule.config([ - '$provide', -function($provide) { - function $LocationDecorator($location, $timeout) { - - $location.__hash = $location.hash; - //Fix: when window.location.hash is set, the scrollable area - //found nearest to body's scrollTop is set to scroll to an element - //with that ID. - $location.hash = function(value) { - if (isDefined(value) && value.length > 0) { - $timeout(function() { - var scroll = document.querySelector('.scroll-content'); - if (scroll) { - scroll.scrollTop = 0; - } - }, 0, false); - } - return $location.__hash(value); - }; - - return $location; - } - - $provide.decorator('$location', ['$delegate', '$timeout', $LocationDecorator]); -}]); - -IonicModule - -.controller('$ionicHeaderBar', [ - '$scope', - '$element', - '$attrs', - '$q', - '$ionicConfig', - '$ionicHistory', -function($scope, $element, $attrs, $q, $ionicConfig, $ionicHistory) { - var TITLE = 'title'; - var BACK_TEXT = 'back-text'; - var BACK_BUTTON = 'back-button'; - var DEFAULT_TITLE = 'default-title'; - var PREVIOUS_TITLE = 'previous-title'; - var HIDE = 'hide'; - - var self = this; - var titleText = ''; - var previousTitleText = ''; - var titleLeft = 0; - var titleRight = 0; - var titleCss = ''; - var isBackEnabled = false; - var isBackShown = true; - var isNavBackShown = true; - var isBackElementShown = false; - var titleTextWidth = 0; - - - self.beforeEnter = function(viewData) { - $scope.$broadcast('$ionicView.beforeEnter', viewData); - }; - - - self.title = function(newTitleText) { - if (arguments.length && newTitleText !== titleText) { - getEle(TITLE).innerHTML = newTitleText; - titleText = newTitleText; - titleTextWidth = 0; - } - return titleText; - }; - - - self.enableBack = function(shouldEnable, disableReset) { - // whether or not the back button show be visible, according - // to the navigation and history - if (arguments.length) { - isBackEnabled = shouldEnable; - if (!disableReset) self.updateBackButton(); - } - return isBackEnabled; - }; - - - self.showBack = function(shouldShow, disableReset) { - // different from enableBack() because this will always have the back - // visually hidden if false, even if the history says it should show - if (arguments.length) { - isBackShown = shouldShow; - if (!disableReset) self.updateBackButton(); - } - return isBackShown; - }; - - - self.showNavBack = function(shouldShow) { - // different from showBack() because this is for the entire nav bar's - // setting for all of it's child headers. For internal use. - isNavBackShown = shouldShow; - self.updateBackButton(); - }; - - - self.updateBackButton = function() { - var ele; - if ((isBackShown && isNavBackShown && isBackEnabled) !== isBackElementShown) { - isBackElementShown = isBackShown && isNavBackShown && isBackEnabled; - ele = getEle(BACK_BUTTON); - ele && ele.classList[ isBackElementShown ? 'remove' : 'add' ](HIDE); - } - - if (isBackEnabled) { - ele = ele || getEle(BACK_BUTTON); - if (ele) { - if (self.backButtonIcon !== $ionicConfig.backButton.icon()) { - ele = getEle(BACK_BUTTON + ' .icon'); - if (ele) { - self.backButtonIcon = $ionicConfig.backButton.icon(); - ele.className = 'icon ' + self.backButtonIcon; - } - } - - if (self.backButtonText !== $ionicConfig.backButton.text()) { - ele = getEle(BACK_BUTTON + ' .back-text'); - if (ele) { - ele.textContent = self.backButtonText = $ionicConfig.backButton.text(); - } - } - } - } - }; - - - self.titleTextWidth = function() { - if (!titleTextWidth) { - var bounds = ionic.DomUtil.getTextBounds(getEle(TITLE)); - titleTextWidth = Math.min(bounds && bounds.width || 30); - } - return titleTextWidth; - }; - - - self.titleWidth = function() { - var titleWidth = self.titleTextWidth(); - var offsetWidth = getEle(TITLE).offsetWidth; - if (offsetWidth < titleWidth) { - titleWidth = offsetWidth + (titleLeft - titleRight - 5); - } - return titleWidth; - }; - - - self.titleTextX = function() { - return ($element[0].offsetWidth / 2) - (self.titleWidth() / 2); - }; - - - self.titleLeftRight = function() { - return titleLeft - titleRight; - }; - - - self.backButtonTextLeft = function() { - var offsetLeft = 0; - var ele = getEle(BACK_TEXT); - while (ele) { - offsetLeft += ele.offsetLeft; - ele = ele.parentElement; - } - return offsetLeft; - }; - - - self.resetBackButton = function(viewData) { - if ($ionicConfig.backButton.previousTitleText()) { - var previousTitleEle = getEle(PREVIOUS_TITLE); - if (previousTitleEle) { - previousTitleEle.classList.remove(HIDE); - - var view = (viewData && $ionicHistory.getViewById(viewData.viewId)); - var newPreviousTitleText = $ionicHistory.backTitle(view); - - if (newPreviousTitleText !== previousTitleText) { - previousTitleText = previousTitleEle.innerHTML = newPreviousTitleText; - } - } - var defaultTitleEle = getEle(DEFAULT_TITLE); - if (defaultTitleEle) { - defaultTitleEle.classList.remove(HIDE); - } - } - }; - - - self.align = function(textAlign) { - var titleEle = getEle(TITLE); - - textAlign = textAlign || $attrs.alignTitle || $ionicConfig.navBar.alignTitle(); - - var widths = self.calcWidths(textAlign, false); - - if (isBackShown && previousTitleText && $ionicConfig.backButton.previousTitleText()) { - var previousTitleWidths = self.calcWidths(textAlign, true); - - var availableTitleWidth = $element[0].offsetWidth - previousTitleWidths.titleLeft - previousTitleWidths.titleRight; - - if (self.titleTextWidth() <= availableTitleWidth) { - widths = previousTitleWidths; - } - } - - return self.updatePositions(titleEle, widths.titleLeft, widths.titleRight, widths.buttonsLeft, widths.buttonsRight, widths.css, widths.showPrevTitle); - }; - - - self.calcWidths = function(textAlign, isPreviousTitle) { - var titleEle = getEle(TITLE); - var backBtnEle = getEle(BACK_BUTTON); - var x, y, z, b, c, d, childSize, bounds; - var childNodes = $element[0].childNodes; - var buttonsLeft = 0; - var buttonsRight = 0; - var isCountRightOfTitle; - var updateTitleLeft = 0; - var updateTitleRight = 0; - var updateCss = ''; - var backButtonWidth = 0; - - // Compute how wide the left children are - // Skip all titles (there may still be two titles, one leaving the dom) - // Once we encounter a titleEle, realize we are now counting the right-buttons, not left - for (x = 0; x < childNodes.length; x++) { - c = childNodes[x]; - - childSize = 0; - if (c.nodeType == 1) { - // element node - if (c === titleEle) { - isCountRightOfTitle = true; - continue; - } - - if (c.classList.contains(HIDE)) { - continue; - } - - if (isBackShown && c === backBtnEle) { - - for (y = 0; y < c.childNodes.length; y++) { - b = c.childNodes[y]; - - if (b.nodeType == 1) { - - if (b.classList.contains(BACK_TEXT)) { - for (z = 0; z < b.children.length; z++) { - d = b.children[z]; - - if (isPreviousTitle) { - if (d.classList.contains(DEFAULT_TITLE)) continue; - backButtonWidth += d.offsetWidth; - } else { - if (d.classList.contains(PREVIOUS_TITLE)) continue; - backButtonWidth += d.offsetWidth; - } - } - - } else { - backButtonWidth += b.offsetWidth; - } - - } else if (b.nodeType == 3 && b.nodeValue.trim()) { - bounds = ionic.DomUtil.getTextBounds(b); - backButtonWidth += bounds && bounds.width || 0; - } - - } - childSize = backButtonWidth || c.offsetWidth; - - } else { - // not the title, not the back button, not a hidden element - childSize = c.offsetWidth; - } - - } else if (c.nodeType == 3 && c.nodeValue.trim()) { - // text node - bounds = ionic.DomUtil.getTextBounds(c); - childSize = bounds && bounds.width || 0; - } - - if (isCountRightOfTitle) { - buttonsRight += childSize; - } else { - buttonsLeft += childSize; - } - } - - // Size and align the header titleEle based on the sizes of the left and - // right children, and the desired alignment mode - if (textAlign == 'left') { - updateCss = 'title-left'; - if (buttonsLeft) { - updateTitleLeft = buttonsLeft + 15; - } - if (buttonsRight) { - updateTitleRight = buttonsRight + 15; - } - - } else if (textAlign == 'right') { - updateCss = 'title-right'; - if (buttonsLeft) { - updateTitleLeft = buttonsLeft + 15; - } - if (buttonsRight) { - updateTitleRight = buttonsRight + 15; - } - - } else { - // center the default - var margin = Math.max(buttonsLeft, buttonsRight) + 10; - if (margin > 10) { - updateTitleLeft = updateTitleRight = margin; - } - } - - return { - backButtonWidth: backButtonWidth, - buttonsLeft: buttonsLeft, - buttonsRight: buttonsRight, - titleLeft: updateTitleLeft, - titleRight: updateTitleRight, - showPrevTitle: isPreviousTitle, - css: updateCss - }; - }; - - - self.updatePositions = function(titleEle, updateTitleLeft, updateTitleRight, buttonsLeft, buttonsRight, updateCss, showPreviousTitle) { - var deferred = $q.defer(); - - // only make DOM updates when there are actual changes - if (titleEle) { - if (updateTitleLeft !== titleLeft) { - titleEle.style.left = updateTitleLeft ? updateTitleLeft + 'px' : ''; - titleLeft = updateTitleLeft; - } - if (updateTitleRight !== titleRight) { - titleEle.style.right = updateTitleRight ? updateTitleRight + 'px' : ''; - titleRight = updateTitleRight; - } - - if (updateCss !== titleCss) { - updateCss && titleEle.classList.add(updateCss); - titleCss && titleEle.classList.remove(titleCss); - titleCss = updateCss; - } - } - - if ($ionicConfig.backButton.previousTitleText()) { - var prevTitle = getEle(PREVIOUS_TITLE); - var defaultTitle = getEle(DEFAULT_TITLE); - - prevTitle && prevTitle.classList[ showPreviousTitle ? 'remove' : 'add'](HIDE); - defaultTitle && defaultTitle.classList[ showPreviousTitle ? 'add' : 'remove'](HIDE); - } - - ionic.requestAnimationFrame(function() { - if (titleEle && titleEle.offsetWidth + 10 < titleEle.scrollWidth) { - var minRight = buttonsRight + 5; - var testRight = $element[0].offsetWidth - titleLeft - self.titleTextWidth() - 20; - updateTitleRight = testRight < minRight ? minRight : testRight; - if (updateTitleRight !== titleRight) { - titleEle.style.right = updateTitleRight + 'px'; - titleRight = updateTitleRight; - } - } - deferred.resolve(); - }); - - return deferred.promise; - }; - - - self.setCss = function(elementClassname, css) { - ionic.DomUtil.cachedStyles(getEle(elementClassname), css); - }; - - - var eleCache = {}; - function getEle(className) { - if (!eleCache[className]) { - eleCache[className] = $element[0].querySelector('.' + className); - } - return eleCache[className]; - } - - - $scope.$on('$destroy', function() { - for (var n in eleCache) eleCache[n] = null; - }); - -}]); - -IonicModule -.controller('$ionInfiniteScroll', [ - '$scope', - '$attrs', - '$element', - '$timeout', -function($scope, $attrs, $element, $timeout) { - var self = this; - self.isLoading = false; - - $scope.icon = function() { - return isDefined($attrs.icon) ? $attrs.icon : 'ion-load-d'; - }; - - $scope.spinner = function() { - return isDefined($attrs.spinner) ? $attrs.spinner : ''; - }; - - $scope.$on('scroll.infiniteScrollComplete', function() { - finishInfiniteScroll(); - }); - - $scope.$on('$destroy', function() { - if (self.scrollCtrl && self.scrollCtrl.$element) self.scrollCtrl.$element.off('scroll', self.checkBounds); - if (self.scrollEl && self.scrollEl.removeEventListener) { - self.scrollEl.removeEventListener('scroll', self.checkBounds); - } - }); - - // debounce checking infinite scroll events - self.checkBounds = ionic.Utils.throttle(checkInfiniteBounds, 300); - - function onInfinite() { - ionic.requestAnimationFrame(function() { - $element[0].classList.add('active'); - }); - self.isLoading = true; - $scope.$parent && $scope.$parent.$apply($attrs.onInfinite || ''); - } - - function finishInfiniteScroll() { - ionic.requestAnimationFrame(function() { - $element[0].classList.remove('active'); - }); - $timeout(function() { - if (self.jsScrolling) self.scrollView.resize(); - // only check bounds again immediately if the page isn't cached (scroll el has height) - if ((self.jsScrolling && self.scrollView.__container && self.scrollView.__container.offsetHeight > 0) || - !self.jsScrolling) { - self.checkBounds(); - } - }, 30, false); - self.isLoading = false; - } - - // check if we've scrolled far enough to trigger an infinite scroll - function checkInfiniteBounds() { - if (self.isLoading) return; - var maxScroll = {}; - - if (self.jsScrolling) { - maxScroll = self.getJSMaxScroll(); - var scrollValues = self.scrollView.getValues(); - if ((maxScroll.left !== -1 && scrollValues.left >= maxScroll.left) || - (maxScroll.top !== -1 && scrollValues.top >= maxScroll.top)) { - onInfinite(); - } - } else { - maxScroll = self.getNativeMaxScroll(); - if (( - maxScroll.left !== -1 && - self.scrollEl.scrollLeft >= maxScroll.left - self.scrollEl.clientWidth - ) || ( - maxScroll.top !== -1 && - self.scrollEl.scrollTop >= maxScroll.top - self.scrollEl.clientHeight - )) { - onInfinite(); - } - } - } - - // determine the threshold at which we should fire an infinite scroll - // note: this gets processed every scroll event, can it be cached? - self.getJSMaxScroll = function() { - var maxValues = self.scrollView.getScrollMax(); - return { - left: self.scrollView.options.scrollingX ? - calculateMaxValue(maxValues.left) : - -1, - top: self.scrollView.options.scrollingY ? - calculateMaxValue(maxValues.top) : - -1 - }; - }; - - self.getNativeMaxScroll = function() { - var maxValues = { - left: self.scrollEl.scrollWidth, - top: self.scrollEl.scrollHeight - }; - var computedStyle = window.getComputedStyle(self.scrollEl) || {}; - return { - left: maxValues.left && - (computedStyle.overflowX === 'scroll' || - computedStyle.overflowX === 'auto' || - self.scrollEl.style['overflow-x'] === 'scroll') ? - calculateMaxValue(maxValues.left) : -1, - top: maxValues.top && - (computedStyle.overflowY === 'scroll' || - computedStyle.overflowY === 'auto' || - self.scrollEl.style['overflow-y'] === 'scroll' ) ? - calculateMaxValue(maxValues.top) : -1 - }; - }; - - // determine pixel refresh distance based on % or value - function calculateMaxValue(maximum) { - var distance = ($attrs.distance || '2.5%').trim(); - var isPercent = distance.indexOf('%') !== -1; - return isPercent ? - maximum * (1 - parseFloat(distance) / 100) : - maximum - parseFloat(distance); - } - - //for testing - self.__finishInfiniteScroll = finishInfiniteScroll; - -}]); - -/** - * @ngdoc service - * @name $ionicListDelegate - * @module ionic - * - * @description - * Delegate for controlling the {@link ionic.directive:ionList} directive. - * - * Methods called directly on the $ionicListDelegate service will control all lists. - * Use the {@link ionic.service:$ionicListDelegate#$getByHandle $getByHandle} - * method to control specific ionList instances. - * - * @usage - * ```html - * {% raw %} - * - * - * - * - * Hello, {{i}}! - * - * - * - * - * {% endraw %} - * ``` - - * ```js - * function MyCtrl($scope, $ionicListDelegate) { - * $scope.showDeleteButtons = function() { - * $ionicListDelegate.showDelete(true); - * }; - * } - * ``` - */ -IonicModule.service('$ionicListDelegate', ionic.DelegateService([ - /** - * @ngdoc method - * @name $ionicListDelegate#showReorder - * @param {boolean=} showReorder Set whether or not this list is showing its reorder buttons. - * @returns {boolean} Whether the reorder buttons are shown. - */ - 'showReorder', - /** - * @ngdoc method - * @name $ionicListDelegate#showDelete - * @param {boolean=} showDelete Set whether or not this list is showing its delete buttons. - * @returns {boolean} Whether the delete buttons are shown. - */ - 'showDelete', - /** - * @ngdoc method - * @name $ionicListDelegate#canSwipeItems - * @param {boolean=} canSwipeItems Set whether or not this list is able to swipe to show - * option buttons. - * @returns {boolean} Whether the list is able to swipe to show option buttons. - */ - 'canSwipeItems', - /** - * @ngdoc method - * @name $ionicListDelegate#closeOptionButtons - * @description Closes any option buttons on the list that are swiped open. - */ - 'closeOptionButtons' - /** - * @ngdoc method - * @name $ionicListDelegate#$getByHandle - * @param {string} handle - * @returns `delegateInstance` A delegate instance that controls only the - * {@link ionic.directive:ionList} directives with `delegate-handle` matching - * the given handle. - * - * Example: `$ionicListDelegate.$getByHandle('my-handle').showReorder(true);` - */ -])) - -.controller('$ionicList', [ - '$scope', - '$attrs', - '$ionicListDelegate', - '$ionicHistory', -function($scope, $attrs, $ionicListDelegate, $ionicHistory) { - var self = this; - var isSwipeable = true; - var isReorderShown = false; - var isDeleteShown = false; - - var deregisterInstance = $ionicListDelegate._registerInstance( - self, $attrs.delegateHandle, function() { - return $ionicHistory.isActiveScope($scope); - } - ); - $scope.$on('$destroy', deregisterInstance); - - self.showReorder = function(show) { - if (arguments.length) { - isReorderShown = !!show; - } - return isReorderShown; - }; - - self.showDelete = function(show) { - if (arguments.length) { - isDeleteShown = !!show; - } - return isDeleteShown; - }; - - self.canSwipeItems = function(can) { - if (arguments.length) { - isSwipeable = !!can; - } - return isSwipeable; - }; - - self.closeOptionButtons = function() { - self.listView && self.listView.clearDragEffects(); - }; -}]); - -IonicModule - -.controller('$ionicNavBar', [ - '$scope', - '$element', - '$attrs', - '$compile', - '$timeout', - '$ionicNavBarDelegate', - '$ionicConfig', - '$ionicHistory', -function($scope, $element, $attrs, $compile, $timeout, $ionicNavBarDelegate, $ionicConfig, $ionicHistory) { - - var CSS_HIDE = 'hide'; - var DATA_NAV_BAR_CTRL = '$ionNavBarController'; - var PRIMARY_BUTTONS = 'primaryButtons'; - var SECONDARY_BUTTONS = 'secondaryButtons'; - var BACK_BUTTON = 'backButton'; - var ITEM_TYPES = 'primaryButtons secondaryButtons leftButtons rightButtons title'.split(' '); - - var self = this; - var headerBars = []; - var navElementHtml = {}; - var isVisible = true; - var queuedTransitionStart, queuedTransitionEnd, latestTransitionId; - - $element.parent().data(DATA_NAV_BAR_CTRL, self); - - var delegateHandle = $attrs.delegateHandle || 'navBar' + ionic.Utils.nextUid(); - - var deregisterInstance = $ionicNavBarDelegate._registerInstance(self, delegateHandle); - - - self.init = function() { - $element.addClass('nav-bar-container'); - ionic.DomUtil.cachedAttr($element, 'nav-bar-transition', $ionicConfig.views.transition()); - - // create two nav bar blocks which will trade out which one is shown - self.createHeaderBar(false); - self.createHeaderBar(true); - - $scope.$emit('ionNavBar.init', delegateHandle); - }; - - - self.createHeaderBar = function(isActive) { - var containerEle = jqLite('