';
/**
* @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.
*
* @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',
'$document',
'$compile',
'$ionicPlatform',
function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $document, $compile, $ionicPlatform) {
//TODO allow this to be configured
var config = {
stackPushDelay: 50
};
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 correspondingbutton 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.
* 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.
* 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.
* 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.
* 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
* 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 popupPromise = $ionicTemplateLoader.compile({
template: POPUP_TPL,
scope: options.scope && options.scope.$new(),
appendTo: $document[0].body
});
var contentPromise = options.templateUrl ?
$ionicTemplateLoader.load(options.templateUrl) :
$q.when(options.template || options.content || '');
return $q.all([popupPromise, contentPromise])
.then(function(results) {
var self = results[0];
var content = results[1];
var responseDeferred = $q.defer();
self.responseDeferred = responseDeferred;
//Can't ng-bind-html for popup-body because it can be insecure html
//(eg an input in case of prompt)
var body = jqLite(self.element[0].querySelector('.popup-body'));
if (content) {
body.html(content);
$compile(body.contents())(self.scope);
} else {
body.remove();
}
extend(self.scope, {
title: options.title,
buttons: options.buttons,
subTitle: options.subTitle,
$buttonTapped: function(button, event) {
var result = (button.onTap || angular.noop)(event);
event = event.originalEvent || event; //jquery events
if (!event.defaultPrevented) {
responseDeferred.resolve(result);
}
}
});
self.show = function() {
if (self.isShown) return;
self.isShown = true;
ionic.requestAnimationFrame(function() {
//if hidden while waiting for raf, don't show
if (!self.isShown) return;
//if the popup is taller than the window, make the popup body scrollable
if(self.element[0].offsetHeight > window.innerHeight - 20){
self.element[0].style.height = window.innerHeight - 20+'px';
popupBody = self.element[0].querySelectorAll('.popup-body');
popupHead = self.element[0].querySelectorAll('.popup-head');
popupButtons = self.element[0].querySelectorAll('.popup-buttons');
self.element.addClass('popup-tall');
newHeight = window.innerHeight - popupHead[0].offsetHeight - popupButtons[0].offsetHeight -20;
popupBody[0].style.height = newHeight + 'px';
}
self.element.removeClass('popup-hidden');
self.element.addClass('popup-showing active');
ionic.DomUtil.centerElementByMarginTwice(self.element[0]);
focusInputOrButton(self.element);
});
};
self.hide = function(callback) {
callback = callback || angular.noop;
if (!self.isShown) return callback();
self.isShown = false;
self.element.removeClass('active');
self.element.addClass('popup-hidden');
$timeout(callback, 250);
};
self.remove = function() {
if (self.removed) return;
self.hide(function() {
self.element.remove();
self.scope.$destroy();
});
self.removed = true;
};
return self;
});
}
function onHardwareBackButton(e) {
popupStack[0] && popupStack[0].responseDeferred.resolve();
}
function showPopup(options) {
var popupPromise = $ionicPopup._createPopup(options);
var previousPopup = popupStack[0];
if (previousPopup) {
previousPopup.hide();
}
var resultPromise = $timeout(angular.noop, previousPopup ? config.stackPushDelay : 0)
.then(function() { return popupPromise; })
.then(function(popup) {
if (!previousPopup) {
//Add popup-open & backdrop if this is first popup
document.body.classList.add('popup-open');
$ionicBackdrop.retain();
$ionicPopup._backButtonActionDone = $ionicPlatform.registerBackButtonAction(
onHardwareBackButton,
PLATFORM_BACK_BUTTON_PRIORITY_POPUP
);
}
popupStack.unshift(popup);
popup.show();
//DEPRECATED: notify the promise with an object with a close method
popup.responseDeferred.notify({
close: resultPromise.close
});
return popup.responseDeferred.promise.then(function(result) {
var index = popupStack.indexOf(popup);
if (index !== -1) {
popupStack.splice(index, 1);
}
popup.remove();
var previousPopup = popupStack[0];
if (previousPopup) {
previousPopup.show();
} else {
//Remove popup-open & backdrop if this is last popup
document.body.classList.remove('popup-open');
$ionicBackdrop.release();
($ionicPopup._backButtonActionDone || angular.noop)();
}
return result;
});
});
function close(result) {
popupPromise.then(function(popup) {
if (!popup.removed) {
popup.responseDeferred.resolve(result);
}
});
}
resultPromise.close = close;
return resultPromise;
}
function focusInputOrButton(element) {
var focusOn = element[0].querySelector('input[autofocus]');
if (!focusOn) {
focusOn = element[0].querySelector('input');
if (!focusOn) {
var buttons = element[0].querySelectorAll('button');
focusOn = buttons[buttons.length-1];
}
}
if(focusOn) {
focusOn.focus();
}
}
function showAlert(opts) {
return showPopup( extend({
buttons: [{
text: opts.okText || 'OK',
type: opts.okType || 'button-positive',
onTap: function(e) {
return true;
}
}]
}, opts || {}) );
}
function showConfirm(opts) {
return showPopup( extend({
buttons: [{
text: opts.cancelText || 'Cancel' ,
type: opts.cancelType || 'button-default',
onTap: function(e) { return false; }
}, {
text: opts.okText || 'OK',
type: opts.okType || 'button-positive',
onTap: function(e) { return true; }
}]
}, opts || {}) );
}
function showPrompt(opts) {
var scope = $rootScope.$new(true);
scope.data = {};
return showPopup( extend({
template: '',
scope: scope,
buttons: [{
text: opts.cancelText || 'Cancel',
type: opts.cancelType|| 'button-default',
onTap: function(e) {}
}, {
text: opts.okText || 'OK',
type: opts.okType || 'button-positive',
onTap: function(e) {
return scope.data.response || '';
}
}]
}, opts || {}) );
}
}]);