angular.module('plunker', ['ngDialog']);
angular.module('plunker').controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope', 'ngDialog'];
function MainCtrl($scope, ngDialog) {
$scope.callToAction = function () {
var modalHeader = 'Visit note';
var placeholder = 'Leave a comment about the visit that you have just finished.';
var type = 'Form';
// Open modal using ngDialog
ngDialog.open({
template: 'modal.html',
controller: 'ModalCtrl',
controllerAs: 'ctaCtrl',
width: 520,
//closeByDocument: false,
//closeByEscape: false,
preCloseCallback: function (value) {
var nestedConfirmDialog = ngDialog.openConfirm({
template: 'alert-modal.html',
closeByDocument: false,
closeByEscape: false,
className: 'ngdialog-theme-default',
showClose: false,
width: 520,
data: {
modalHeader: 'Discard note?',
modalBody: 'Are you sure you want to discard this note?',
trueOption: 'Keep',
falseOption: 'Discard'
}
});
// NOTE: return the promise from openConfirm
return nestedConfirmDialog;
},
data: {
type: type,
placeholder: placeholder,
modalHeader: modalHeader
}
});
};
$scope.$on('sendModalForm', function (event, args) {
/* Do stuff and then... */
ngDialog.close();
});
}
angular.module('plunker').controller('ModalCtrl', ModalCtrl);
ModalCtrl.$inject = ['$rootScope'];
function ModalCtrl($rootScope) {
var vm = this;
vm.sendModalForm = function (comment) {
$rootScope.$broadcast('sendModalForm', {comment: comment});
};
}
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css" />
</head>
<body ng-controller="MainCtrl">
<div class="container">
<div class="wrapper">
<a class="button" ng-click="callToAction()">Open ngDialog</a>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="ngDialog.js"></script>
<script src="app.js"></script>
</body>
</html>
.container {
position: relative;
display: block;
font-family: 'Lato', Arial, Helvetica, sans-serif;
margin: 1.25rem auto;
max-width: 64rem;
}
.wrapper {
position: relative;
margin: 0;
}
.button {
display: block;
background-color: #FF005B;
border: 0;
border-radius: 0.25rem;
font-size: 1em;
color: #FFF;
cursor: pointer;
outline: none;
padding: 0.875em;
text-align: center;
text-decoration: none;
text-transform: capitalize;
margin: 0 auto;
width: 30%;
}
.button[disabled] {
background-color: #D8D8D8;
}
textarea {
display: inline-block;
background-color: #FFF;
border: none;
border-bottom: 1px solid #D8D8D8;
border-radius: 0;
font-size: 17px;
color: #232129;
outline: none;
padding: 1.25rem;
height: 18.75rem;
width: 100%;
}
textarea:focus {
border-color: #232129;
color: #232129;
}
.input-error {
color: #ECB20A;
}
.link,
.link:visited {
color: #FF005B;
cursor: pointer;
text-decoration: none;
}
.link:active,
.link:visited:active {
color: #d9004d;
}
.link:hover,
.link:visited:hover {
color: #ff2674;
text-decoration: underline;
}
@-webkit-keyframes ngdialog-fadeout {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes ngdialog-fadeout {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@-webkit-keyframes ngdialog-fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes ngdialog-fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.ngdialog {
box-sizing: border-box;
}
.ngdialog *,
.ngdialog *:before,
.ngdialog *:after {
box-sizing: inherit;
}
.ngdialog {
position: fixed;
overflow: auto;
-webkit-overflow-scrolling: touch;
z-index: 10000;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* fix for Scrollbars not clickable on overflow #552 */
background: rgba(0, 0, 0, 0.4);
animation: ngdialog-fadein 0.5s;
/* end fix for Scrollbars not clickable on overflow #552 */
text-align: center;
}
.ngdialog.ngdialog-disabled-animation,
.ngdialog.ngdialog-disabled-animation .ngdialog-overlay,
.ngdialog.ngdialog-disabled-animation .ngdialog-content {
-webkit-animation: none!important;
animation: none!important;
}
.ngdialog-overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
-webkit-backface-visibility: hidden;
-webkit-animation: ngdialog-fadein 0.5s;
animation: ngdialog-fadein 0.5s;
/* fix for Scrollbars not clickable on overflow #552 */
margin-right: 15px;
background: transparent;
/* end fix for Scrollbars not clickable on overflow #552 */
}
.ngdialog-no-overlay {
pointer-events: none;
}
.ngdialog.ngdialog-closing .ngdialog-overlay {
-webkit-backface-visibility: hidden;
-webkit-animation: ngdialog-fadeout 0.5s;
animation: ngdialog-fadeout 0.5s;
}
.ngdialog-content {
background: white;
-webkit-backface-visibility: hidden;
-webkit-animation: ngdialog-fadein 0.5s;
animation: ngdialog-fadein 0.5s;
pointer-events: all;
}
.ngdialog.ngdialog-closing .ngdialog-content {
-webkit-backface-visibility: hidden;
-webkit-animation: ngdialog-fadeout 0.5s;
animation: ngdialog-fadeout 0.5s;
}
.ngdialog-close:before {
font-family: 'Helvetica', Arial, sans-serif;
content: '\00D7';
cursor: pointer;
}
html.ngdialog-open,
body.ngdialog-open {
overflow: hidden;
}
@-webkit-keyframes ngdialog-flyin {
0% {
opacity: 0;
-webkit-transform: translateY(-40px);
transform: translateY(-40px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
@keyframes ngdialog-flyin {
0% {
opacity: 0;
-webkit-transform: translateY(-40px);
transform: translateY(-40px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
@-webkit-keyframes ngdialog-flyout {
0% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
transform: translateY(-40px);
}
}
@keyframes ngdialog-flyout {
0% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
transform: translateY(-40px);
}
}
.ngdialog.ngdialog-theme-default {
padding-bottom: 160px;
padding-top: 160px;
}
.ngdialog.ngdialog-theme-default.ngdialog-closing .ngdialog-content {
-webkit-animation: ngdialog-flyout .5s;
animation: ngdialog-flyout .5s;
}
.ngdialog.ngdialog-theme-default .ngdialog-content {
-webkit-animation: ngdialog-flyin .5s;
animation: ngdialog-flyin .5s;
background: #f0f0f0;
border-radius: 5px;
color: #444;
font-family: 'Helvetica',sans-serif;
font-size: 1.1em;
line-height: 1.5em;
margin: 0 auto;
max-width: 100%;
padding: 1em;
position: relative;
width: 450px;
}
.ngdialog.ngdialog-theme-default .ngdialog-close {
border: none;
background: transparent;
cursor: pointer;
position: absolute;
right: 0;
top: 0;
}
.ngdialog.ngdialog-theme-default .ngdialog-close:before {
display: block;
padding: 3px;
background: transparent;
color: #bbb;
content: '\00D7';
font-size: 26px;
font-weight: 400;
line-height: 26px;
text-align: center;
}
.ngdialog.ngdialog-theme-default .ngdialog-close:hover:before,
.ngdialog.ngdialog-theme-default .ngdialog-close:active:before {
color: #777;
}
.ngdialog.ngdialog-theme-default .ngdialog-message {
margin-bottom: .5em;
}
.ngdialog.ngdialog-theme-default .ngdialog-input {
margin-bottom: 1em;
}
.ngdialog.ngdialog-theme-default .ngdialog-input textarea,
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="text"],
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="password"],
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="email"],
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="url"] {
background: #fff;
border: 0;
border-radius: 3px;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
margin: 0 0 .25em;
min-height: 2.5em;
padding: .25em .67em;
width: 100%;
}
.ngdialog.ngdialog-theme-default .ngdialog-input textarea:focus,
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="text"]:focus,
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="password"]:focus,
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="email"]:focus,
.ngdialog.ngdialog-theme-default .ngdialog-input input[type="url"]:focus {
box-shadow: inset 0 0 0 2px #8dbdf1;
outline: none;
}
.ngdialog.ngdialog-theme-default .ngdialog-buttons {
*zoom: 1;
}
.ngdialog.ngdialog-theme-default .ngdialog-buttons:after {
content: '';
display: table;
clear: both;
}
.ngdialog.ngdialog-theme-default .ngdialog-button {
border: 0;
border-radius: 3px;
cursor: pointer;
float: right;
font-family: inherit;
font-size: .8em;
letter-spacing: .1em;
line-height: 1em;
margin: 0 0 0 .5em;
padding: .75em 2em;
text-transform: uppercase;
}
.ngdialog.ngdialog-theme-default .ngdialog-button:focus {
-webkit-animation: ngdialog-pulse 1.1s infinite;
animation: ngdialog-pulse 1.1s infinite;
outline: none;
}
@media (max-width: 568px) {
.ngdialog.ngdialog-theme-default .ngdialog-button:focus {
-webkit-animation: none;
animation: none;
}
}
.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-primary {
background: #3288e6;
color: #fff;
}
.ngdialog.ngdialog-theme-default .ngdialog-button.ngdialog-button-secondary {
background: #e0e0e0;
color: #777;
}
<article class="modalContainer commentsModal">
<h2 class="modalHeader heading3">
{{ ngDialogData.modalHeader }}
</h2>
<div class="modalBody">
<form
class="commentsForm"
name="commentsForm"
ng-submit="ctaCtrl.sendModalForm(ctaCtrl.userComment)"
novalidate
autocomplete="off"
>
<div class="formField">
<textarea
name="userComment"
class="textareaComments"
autofocus="autofocus"
placeholder="{{ ngDialogData.placeholder }}"
ng-model="ctaCtrl.userComment"
ng-minlength="20"
required
>
{{ ngDialogData.modalComment }}
</textarea>
<div class="input-errors-container" ng-show="commentsForm.userComment.$dirty && commentsForm.userComment.$invalid">
<span class="input-error" ng-show="commentsForm.userComment.$error.required">Please leave a comment about your recent visit.</span>
<span class="input-error" ng-show="commentsForm.userComment.$error.minlength">The comment must be at least 20 characters long.</span>
</div>
</div>
<button class="button submitButton" type="submit" ng-disabled="commentsForm.$pristine">Submit Comment</button>
</form>
</div>
</article>
/**
* @module ngDialog
* @namespace ngDialog
*
* @description
* Modals and popups provider for Angular.js applications. No dependencies. Highly customizable.
* UPGRADE NOTES:
* 1. Closure <button> modified into an <a>
*
* {@link http://likeastore.github.io/ngDialog/}
* @version ngDialog - v1.3.0 - 2017-05-22
*/
(function (root, factory) {
'use strict';
if (typeof module !== 'undefined' && module.exports) {
// CommonJS
if (typeof angular === 'undefined') {
factory(require('angular'));
} else {
factory(angular);
}
module.exports = 'ngDialog';
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['angular'], factory);
} else {
// Global Variables
factory(root.angular);
}
}(this, function (angular) {
'use strict';
var m = angular.module('ngDialog', []);
var $el = angular.element;
var isDef = angular.isDefined;
var style = (document.body || document.documentElement).style;
var animationEndSupport = isDef(style.animation) || isDef(style.WebkitAnimation) || isDef(style.MozAnimation) || isDef(style.MsAnimation) || isDef(style.OAnimation);
var animationEndEvent = 'animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend';
var focusableElementSelector = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]';
var disabledAnimationClass = 'ngdialog-disabled-animation';
var forceElementsReload = { html: false, body: false };
var scopes = {};
var openIdStack = [];
var activeBodyClasses = [];
var keydownIsBound = false;
var openOnePerName = false;
var closeByNavigationDialogStack = [];
var UI_ROUTER_VERSION_LEGACY = 'legacy';
var UI_ROUTER_VERSION_ONE_PLUS = '1.0.0+';
m.provider('ngDialog', function () {
var defaults = this.defaults = {
className: 'ngdialog-theme-default',
appendClassName: '',
disableAnimation: false,
plain: false,
showClose: true,
closeByDocument: true,
closeByEscape: true,
closeByNavigation: false,
appendTo: false,
preCloseCallback: false,
onOpenCallback: false,
overlay: true,
cache: true,
trapFocus: true,
preserveFocus: true,
ariaAuto: true,
ariaRole: null,
ariaLabelledById: null,
ariaLabelledBySelector: null,
ariaDescribedById: null,
ariaDescribedBySelector: null,
bodyClassName: 'ngdialog-open',
width: null,
height: null
};
this.setForceHtmlReload = function (_useIt) {
forceElementsReload.html = _useIt || false;
};
this.setForceBodyReload = function (_useIt) {
forceElementsReload.body = _useIt || false;
};
this.setDefaults = function (newDefaults) {
angular.extend(defaults, newDefaults);
};
this.setOpenOnePerName = function (isOpenOne) {
openOnePerName = isOpenOne || false;
};
var globalID = 0, dialogsCount = 0, closeByDocumentHandler, defers = {};
this.$get = ['$document', '$templateCache', '$compile', '$q', '$http', '$rootScope', '$timeout', '$window', '$controller', '$injector',
function ($document, $templateCache, $compile, $q, $http, $rootScope, $timeout, $window, $controller, $injector) {
var $elements = [];
var privateMethods = {
onDocumentKeydown: function (event) {
if (event.keyCode === 27) {
publicMethods.close('$escape');
}
},
activate: function($dialog) {
var options = $dialog.data('$ngDialogOptions');
if (options.trapFocus) {
$dialog.on('keydown', privateMethods.onTrapFocusKeydown);
// Catch rogue changes (eg. after unfocusing everything by clicking a non-focusable element)
$elements.body.on('keydown', privateMethods.onTrapFocusKeydown);
}
},
deactivate: function ($dialog) {
$dialog.off('keydown', privateMethods.onTrapFocusKeydown);
$elements.body.off('keydown', privateMethods.onTrapFocusKeydown);
},
deactivateAll: function (els) {
angular.forEach(els,function(el) {
var $dialog = angular.element(el);
privateMethods.deactivate($dialog);
});
},
setBodyPadding: function (width) {
var originalBodyPadding = parseInt(($elements.body.css('padding-right') || 0), 10);
$elements.body.css('padding-right', (originalBodyPadding + width) + 'px');
$elements.body.data('ng-dialog-original-padding', originalBodyPadding);
$rootScope.$broadcast('ngDialog.setPadding', width);
},
resetBodyPadding: function () {
var originalBodyPadding = $elements.body.data('ng-dialog-original-padding');
if (originalBodyPadding) {
$elements.body.css('padding-right', originalBodyPadding + 'px');
} else {
$elements.body.css('padding-right', '');
}
$rootScope.$broadcast('ngDialog.setPadding', 0);
},
performCloseDialog: function ($dialog, value) {
var options = $dialog.data('$ngDialogOptions');
var id = $dialog.attr('id');
var scope = scopes[id];
privateMethods.deactivate($dialog);
if (!scope) {
// Already closed
return;
}
if (typeof $window.Hammer !== 'undefined') {
var hammerTime = scope.hammerTime;
hammerTime.off('tap', closeByDocumentHandler);
hammerTime.destroy && hammerTime.destroy();
delete scope.hammerTime;
} else {
$dialog.unbind('click');
}
if (dialogsCount === 1) {
$elements.body.unbind('keydown', privateMethods.onDocumentKeydown);
}
if (!$dialog.hasClass('ngdialog-closing')){
dialogsCount -= 1;
}
var previousFocus = $dialog.data('$ngDialogPreviousFocus');
if (previousFocus && previousFocus.focus) {
previousFocus.focus();
}
$rootScope.$broadcast('ngDialog.closing', $dialog, value);
dialogsCount = dialogsCount < 0 ? 0 : dialogsCount;
if (animationEndSupport && !options.disableAnimation) {
scope.$destroy();
$dialog.unbind(animationEndEvent).bind(animationEndEvent, function () {
privateMethods.closeDialogElement($dialog, value);
}).addClass('ngdialog-closing');
} else {
scope.$destroy();
privateMethods.closeDialogElement($dialog, value);
}
if (defers[id]) {
defers[id].resolve({
id: id,
value: value,
$dialog: $dialog,
remainingDialogs: dialogsCount
});
delete defers[id];
}
if (scopes[id]) {
delete scopes[id];
}
openIdStack.splice(openIdStack.indexOf(id), 1);
if (!openIdStack.length) {
$elements.body.unbind('keydown', privateMethods.onDocumentKeydown);
keydownIsBound = false;
}
if (dialogsCount == 0)
{
closeByDocumentHandler = undefined;
}
},
closeDialogElement: function($dialog, value) {
var options = $dialog.data('$ngDialogOptions');
$dialog.remove();
activeBodyClasses.splice(activeBodyClasses.indexOf(options.bodyClassName), 1);
if (activeBodyClasses.indexOf(options.bodyClassName) === -1) {
$elements.html.removeClass(options.bodyClassName);
$elements.body.removeClass(options.bodyClassName);
}
if (dialogsCount === 0) {
privateMethods.resetBodyPadding();
}
$rootScope.$broadcast('ngDialog.closed', $dialog, value);
},
closeDialog: function ($dialog, value) {
var preCloseCallback = $dialog.data('$ngDialogPreCloseCallback');
if (preCloseCallback && angular.isFunction(preCloseCallback)) {
var preCloseCallbackResult = preCloseCallback.call($dialog, value);
if (angular.isObject(preCloseCallbackResult)) {
if (preCloseCallbackResult.closePromise) {
preCloseCallbackResult.closePromise.then(function () {
privateMethods.performCloseDialog($dialog, value);
}, function () {
return false;
});
} else {
preCloseCallbackResult.then(function () {
privateMethods.performCloseDialog($dialog, value);
}, function () {
return false;
});
}
} else if (preCloseCallbackResult !== false) {
privateMethods.performCloseDialog($dialog, value);
} else {
return false;
}
} else {
privateMethods.performCloseDialog($dialog, value);
}
},
onTrapFocusKeydown: function(ev) {
var el = angular.element(ev.currentTarget);
var $dialog;
if (el.hasClass('ngdialog')) {
$dialog = el;
} else {
$dialog = privateMethods.getActiveDialog();
if ($dialog === null) {
return;
}
}
var isTab = (ev.keyCode === 9);
var backward = (ev.shiftKey === true);
if (isTab) {
privateMethods.handleTab($dialog, ev, backward);
}
},
handleTab: function($dialog, ev, backward) {
var focusableElements = privateMethods.getFocusableElements($dialog);
if (focusableElements.length === 0) {
if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur();
}
return;
}
var currentFocus = document.activeElement;
var focusIndex = Array.prototype.indexOf.call(focusableElements, currentFocus);
var isFocusIndexUnknown = (focusIndex === -1);
var isFirstElementFocused = (focusIndex === 0);
var isLastElementFocused = (focusIndex === focusableElements.length - 1);
var cancelEvent = false;
if (backward) {
if (isFocusIndexUnknown || isFirstElementFocused) {
focusableElements[focusableElements.length - 1].focus();
cancelEvent = true;
}
} else {
if (isFocusIndexUnknown || isLastElementFocused) {
focusableElements[0].focus();
cancelEvent = true;
}
}
if (cancelEvent) {
ev.preventDefault();
ev.stopPropagation();
}
},
autoFocus: function($dialog) {
var dialogEl = $dialog[0];
// Browser's (Chrome 40, Forefix 37, IE 11) don't appear to honor autofocus on the dialog, but we should
var autoFocusEl = dialogEl.querySelector('*[autofocus]');
if (autoFocusEl !== null) {
autoFocusEl.focus();
if (document.activeElement === autoFocusEl) {
return;
}
// Autofocus element might was display: none, so let's continue
}
var focusableElements = privateMethods.getFocusableElements($dialog);
if (focusableElements.length > 0) {
focusableElements[0].focus();
return;
}
// We need to focus something for the screen readers to notice the dialog
var contentElements = privateMethods.filterVisibleElements(dialogEl.querySelectorAll('h1,h2,h3,h4,h5,h6,p,span'));
if (contentElements.length > 0) {
var contentElement = contentElements[0];
$el(contentElement).attr('tabindex', '-1').css('outline', '0');
contentElement.focus();
}
},
getFocusableElements: function ($dialog) {
var dialogEl = $dialog[0];
var rawElements = dialogEl.querySelectorAll(focusableElementSelector);
// Ignore untabbable elements, ie. those with tabindex = -1
var tabbableElements = privateMethods.filterTabbableElements(rawElements);
return privateMethods.filterVisibleElements(tabbableElements);
},
filterTabbableElements: function (els) {
var tabbableFocusableElements = [];
for (var i = 0; i < els.length; i++) {
var el = els[i];
if ($el(el).attr('tabindex') !== '-1') {
tabbableFocusableElements.push(el);
}
}
return tabbableFocusableElements;
},
filterVisibleElements: function (els) {
var visibleFocusableElements = [];
for (var i = 0; i < els.length; i++) {
var el = els[i];
if (el.offsetWidth > 0 || el.offsetHeight > 0) {
visibleFocusableElements.push(el);
}
}
return visibleFocusableElements;
},
getActiveDialog: function () {
var dialogs = document.querySelectorAll('.ngdialog');
if (dialogs.length === 0) {
return null;
}
// TODO: This might be incorrect if there are a mix of open dialogs with different 'appendTo' values
return $el(dialogs[dialogs.length - 1]);
},
applyAriaAttributes: function ($dialog, options) {
if (options.ariaAuto) {
if (!options.ariaRole) {
var detectedRole = (privateMethods.getFocusableElements($dialog).length > 0) ?
'dialog' :
'alertdialog';
options.ariaRole = detectedRole;
}
if (!options.ariaLabelledBySelector) {
options.ariaLabelledBySelector = 'h1,h2,h3,h4,h5,h6';
}
if (!options.ariaDescribedBySelector) {
options.ariaDescribedBySelector = 'article,section,p';
}
}
if (options.ariaRole) {
$dialog.attr('role', options.ariaRole);
}
privateMethods.applyAriaAttribute(
$dialog, 'aria-labelledby', options.ariaLabelledById, options.ariaLabelledBySelector);
privateMethods.applyAriaAttribute(
$dialog, 'aria-describedby', options.ariaDescribedById, options.ariaDescribedBySelector);
},
applyAriaAttribute: function($dialog, attr, id, selector) {
if (id) {
$dialog.attr(attr, id);
return;
}
if (selector) {
var dialogId = $dialog.attr('id');
var firstMatch = $dialog[0].querySelector(selector);
if (!firstMatch) {
return;
}
var generatedId = dialogId + '-' + attr;
$el(firstMatch).attr('id', generatedId);
$dialog.attr(attr, generatedId);
return generatedId;
}
},
detectUIRouter: function() {
// Detect if ui-router module is installed
// Returns ui-router version string if installed
// Otherwise false
if ($injector.has('$transitions')) {
// Only 1.0.0+ ui.router allows us to inject $transitions
return UI_ROUTER_VERSION_ONE_PLUS;
}
else if ($injector.has('$state')) {
// The legacy ui.router allows us to inject $state
return UI_ROUTER_VERSION_LEGACY;
}
return false;
},
getRouterLocationEventName: function() {
if (privateMethods.detectUIRouter()) {
return '$stateChangeStart';
}
return '$locationChangeStart';
}
};
var publicMethods = {
__PRIVATE__: privateMethods,
/*
* @param {Object} options:
* - template {String} - id of ng-template, url for partial, plain string (if enabled)
* - plain {Boolean} - enable plain string templates, default false
* - scope {Object}
* - controller {String}
* - controllerAs {String}
* - className {String} - dialog theme class
* - appendClassName {String} - dialog theme class to be appended to defaults
* - disableAnimation {Boolean} - set to true to disable animation
* - showClose {Boolean} - show close button, default true
* - closeByEscape {Boolean} - default true
* - closeByDocument {Boolean} - default true
* - preCloseCallback {String|Function} - user supplied function name/function called before closing dialog (if set)
* - onOpenCallback {String|Function} - user supplied function name/function called after opening dialog (if set)
* - bodyClassName {String} - class added to body at open dialog
* @return {Object} dialog
*/
open: function (opts) {
var dialogID = null;
opts = opts || {};
if (openOnePerName && opts.name) {
dialogID = opts.name.toLowerCase().replace(/\s/g, '-') + '-dialog';
if (this.isOpen(dialogID)) {
return;
}
}
var options = angular.copy(defaults);
var localID = ++globalID;
dialogID = dialogID || 'ngdialog' + localID;
openIdStack.push(dialogID);
// Merge opts.data with predefined via setDefaults
if (typeof options.data !== 'undefined') {
if (typeof opts.data === 'undefined') {
opts.data = {};
}
opts.data = angular.merge(angular.copy(options.data), opts.data);
}
angular.extend(options, opts);
var defer;
defers[dialogID] = defer = $q.defer();
var scope;
scopes[dialogID] = scope = angular.isObject(options.scope) ? options.scope.$new() : $rootScope.$new();
var $dialog, $dialogParent, $dialogContent;
var resolve = angular.extend({}, options.resolve);
angular.forEach(resolve, function (value, key) {
resolve[key] = angular.isString(value) ? $injector.get(value) : $injector.invoke(value, null, null, key);
});
$q.all({
template: loadTemplate(options.template || options.templateUrl),
locals: $q.all(resolve)
}).then(function (setup) {
var template = setup.template,
locals = setup.locals;
if (options.showClose) {
template += '<a aria-label="Dismiss" class="ngdialog-close"></a>';
}
var hasOverlayClass = options.overlay ? '' : ' ngdialog-no-overlay';
$dialog = $el('<div id="' + dialogID + '" class="ngdialog' + hasOverlayClass + '"></div>');
$dialog.html((options.overlay ?
'<div class="ngdialog-overlay"></div><div class="ngdialog-content" role="document">' + template + '</div>' :
'<div class="ngdialog-content" role="document">' + template + '</div>'));
$dialog.data('$ngDialogOptions', options);
scope.ngDialogId = dialogID;
if (options.data && angular.isString(options.data)) {
var firstLetter = options.data.replace(/^\s*/, '')[0];
scope.ngDialogData = (firstLetter === '{' || firstLetter === '[') ? angular.fromJson(options.data) : new String(options.data);
scope.ngDialogData.ngDialogId = dialogID;
} else if (options.data && angular.isObject(options.data)) {
scope.ngDialogData = options.data;
scope.ngDialogData.ngDialogId = dialogID;
}
if (options.className) {
$dialog.addClass(options.className);
}
if (options.appendClassName) {
$dialog.addClass(options.appendClassName);
}
if (options.width) {
$dialogContent = $dialog[0].querySelector('.ngdialog-content');
if (angular.isString(options.width)) {
$dialogContent.style.width = options.width;
} else {
$dialogContent.style.width = options.width + 'px';
}
}
if (options.height) {
$dialogContent = $dialog[0].querySelector('.ngdialog-content');
if (angular.isString(options.height)) {
$dialogContent.style.height = options.height;
} else {
$dialogContent.style.height = options.height + 'px';
}
}
if (options.disableAnimation) {
$dialog.addClass(disabledAnimationClass);
}
if (options.appendTo && angular.isString(options.appendTo)) {
$dialogParent = angular.element(document.querySelector(options.appendTo));
} else {
$dialogParent = $elements.body;
}
privateMethods.applyAriaAttributes($dialog, options);
[
{ name: '$ngDialogPreCloseCallback', value: options.preCloseCallback },
{ name: '$ngDialogOnOpenCallback', value: options.onOpenCallback }
].forEach(function (option) {
if (option.value) {
var callback;
if (angular.isFunction(option.value)) {
callback = option.value;
} else if (angular.isString(option.value)) {
if (scope) {
if (angular.isFunction(scope[option.value])) {
callback = scope[option.value];
} else if (scope.$parent && angular.isFunction(scope.$parent[option.value])) {
callback = scope.$parent[option.value];
} else if ($rootScope && angular.isFunction($rootScope[option.value])) {
callback = $rootScope[option.value];
}
}
}
if (callback) {
$dialog.data(option.name, callback);
}
}
});
scope.closeThisDialog = function (value) {
privateMethods.closeDialog($dialog, value);
};
if (options.controller && (angular.isString(options.controller) || angular.isArray(options.controller) || angular.isFunction(options.controller))) {
var label;
if (options.controllerAs && angular.isString(options.controllerAs)) {
label = options.controllerAs;
}
var controllerInstance = $controller(options.controller, angular.extend(
locals,
{
$scope: scope,
$element: $dialog
}),
true,
label
);
if(options.bindToController) {
angular.extend(controllerInstance.instance, {ngDialogId: scope.ngDialogId, ngDialogData: scope.ngDialogData, closeThisDialog: scope.closeThisDialog, confirm: scope.confirm});
}
if(typeof controllerInstance === 'function'){
$dialog.data('$ngDialogControllerController', controllerInstance());
} else {
$dialog.data('$ngDialogControllerController', controllerInstance);
}
}
$timeout(function () {
var $activeDialogs = document.querySelectorAll('.ngdialog');
privateMethods.deactivateAll($activeDialogs);
$compile($dialog)(scope);
var widthDiffs = $window.innerWidth - $elements.body.prop('clientWidth');
$elements.html.addClass(options.bodyClassName);
$elements.body.addClass(options.bodyClassName);
activeBodyClasses.push(options.bodyClassName);
var scrollBarWidth = widthDiffs - ($window.innerWidth - $elements.body.prop('clientWidth'));
if (scrollBarWidth > 0) {
privateMethods.setBodyPadding(scrollBarWidth);
}
$dialogParent.append($dialog);
privateMethods.activate($dialog);
if (options.trapFocus) {
privateMethods.autoFocus($dialog);
}
if (options.name) {
$rootScope.$broadcast('ngDialog.opened', {dialog: $dialog, name: options.name});
} else {
$rootScope.$broadcast('ngDialog.opened', $dialog);
}
var onOpenCallback = $dialog.data('$ngDialogOnOpenCallback');
if (onOpenCallback && angular.isFunction(onOpenCallback)) {
onOpenCallback.call($dialog);
}
});
if (!keydownIsBound) {
$elements.body.bind('keydown', privateMethods.onDocumentKeydown);
keydownIsBound = true;
}
if (options.closeByNavigation) {
closeByNavigationDialogStack.push($dialog);
}
if (options.preserveFocus) {
$dialog.data('$ngDialogPreviousFocus', document.activeElement);
}
closeByDocumentHandler = function (event) {
var isOverlay = options.closeByDocument ? $el(event.target).hasClass('ngdialog-overlay') : false;
var isCloseBtn = $el(event.target).hasClass('ngdialog-close');
if (isOverlay || isCloseBtn) {
publicMethods.close($dialog.attr('id'), isCloseBtn ? '$closeButton' : '$document');
}
};
if (typeof $window.Hammer !== 'undefined') {
var hammerTime = scope.hammerTime = $window.Hammer($dialog[0]);
hammerTime.on('tap', closeByDocumentHandler);
} else {
$dialog.bind('click', closeByDocumentHandler);
}
dialogsCount += 1;
return publicMethods;
});
return {
id: dialogID,
closePromise: defer.promise,
close: function (value) {
privateMethods.closeDialog($dialog, value);
}
};
function loadTemplateUrl (tmpl, config) {
var config = config || {};
config.headers = config.headers || {};
angular.extend(config.headers, {'Accept': 'text/html'});
$rootScope.$broadcast('ngDialog.templateLoading', tmpl);
return $http.get(tmpl, config).then(function(res) {
$rootScope.$broadcast('ngDialog.templateLoaded', tmpl);
return res.data || '';
});
}
function loadTemplate (tmpl) {
if (!tmpl) {
return 'Empty template';
}
if (angular.isString(tmpl) && options.plain) {
return tmpl;
}
if (typeof options.cache === 'boolean' && !options.cache) {
return loadTemplateUrl(tmpl, {cache: false});
}
return loadTemplateUrl(tmpl, {cache: $templateCache});
}
},
/*
* @param {Object} options:
* - template {String} - id of ng-template, url for partial, plain string (if enabled)
* - plain {Boolean} - enable plain string templates, default false
* - name {String}
* - scope {Object}
* - controller {String}
* - controllerAs {String}
* - className {String} - dialog theme class
* - appendClassName {String} - dialog theme class to be appended to defaults
* - showClose {Boolean} - show close button, default true
* - closeByEscape {Boolean} - default false
* - closeByDocument {Boolean} - default false
* - preCloseCallback {String|Function} - user supplied function name/function called before closing dialog (if set); not called on confirm
* - bodyClassName {String} - class added to body at open dialog
*
* @return {Object} dialog
*/
openConfirm: function (opts) {
var defer = $q.defer();
var options = angular.copy(defaults);
opts = opts || {};
// Merge opts.data with predefined via setDefaults
if (typeof options.data !== 'undefined') {
if (typeof opts.data === 'undefined') {
opts.data = {};
}
opts.data = angular.merge(angular.copy(options.data), opts.data);
}
angular.extend(options, opts);
options.scope = angular.isObject(options.scope) ? options.scope.$new() : $rootScope.$new();
options.scope.confirm = function (value) {
defer.resolve(value);
var $dialog = $el(document.getElementById(openResult.id));
privateMethods.performCloseDialog($dialog, value);
};
var openResult = publicMethods.open(options);
if (openResult) {
openResult.closePromise.then(function (data) {
if (data) {
return defer.reject(data.value);
}
return defer.reject();
});
return defer.promise;
}
},
isOpen: function(id) {
var $dialog = $el(document.getElementById(id));
return $dialog.length > 0;
},
/*
* @param {String} id
* @return {Object} dialog
*/
close: function (id, value) {
var $dialog = $el(document.getElementById(id));
if ($dialog.length) {
privateMethods.closeDialog($dialog, value);
} else {
if (id === '$escape') {
var topDialogId = openIdStack[openIdStack.length - 1];
$dialog = $el(document.getElementById(topDialogId));
if ($dialog.data('$ngDialogOptions').closeByEscape) {
privateMethods.closeDialog($dialog, '$escape');
}
} else {
publicMethods.closeAll(value);
}
}
return publicMethods;
},
closeAll: function (value) {
var $all = document.querySelectorAll('.ngdialog');
// Reverse order to ensure focus restoration works as expected
for (var i = $all.length - 1; i >= 0; i--) {
var dialog = $all[i];
privateMethods.closeDialog($el(dialog), value);
}
},
getOpenDialogs: function() {
return openIdStack;
},
getDefaults: function () {
return defaults;
}
};
angular.forEach(
['html', 'body'],
function(elementName) {
$elements[elementName] = $document.find(elementName);
if (forceElementsReload[elementName]) {
var eventName = privateMethods.getRouterLocationEventName();
$rootScope.$on(eventName, function () {
$elements[elementName] = $document.find(elementName);
});
}
}
);
// Listen to navigation events to close dialog
var uiRouterVersion = privateMethods.detectUIRouter();
if (uiRouterVersion === UI_ROUTER_VERSION_ONE_PLUS) {
var $transitions = $injector.get('$transitions');
$transitions.onStart({}, function (trans) {
while (closeByNavigationDialogStack.length > 0) {
var toCloseDialog = closeByNavigationDialogStack.pop();
if (privateMethods.closeDialog(toCloseDialog) === false) {
return false;
}
}
});
}
else {
var eventName = uiRouterVersion === UI_ROUTER_VERSION_LEGACY ? '$stateChangeStart' : '$locationChangeStart';
$rootScope.$on(eventName, function ($event) {
while (closeByNavigationDialogStack.length > 0) {
var toCloseDialog = closeByNavigationDialogStack.pop();
if (privateMethods.closeDialog(toCloseDialog) === false) {
$event.preventDefault();
}
}
});
}
return publicMethods;
}];
});
m.directive('ngDialog', ['ngDialog', function (ngDialog) {
return {
restrict: 'A',
scope: {
ngDialogScope: '='
},
link: function (scope, elem, attrs) {
elem.on('click', function (e) {
e.preventDefault();
var ngDialogScope = angular.isDefined(scope.ngDialogScope) ? scope.ngDialogScope : 'noScope';
angular.isDefined(attrs.ngDialogClosePrevious) && ngDialog.close(attrs.ngDialogClosePrevious);
var defaults = ngDialog.getDefaults();
ngDialog.open({
template: attrs.ngDialog,
className: attrs.ngDialogClass || defaults.className,
appendClassName: attrs.ngDialogAppendClass,
controller: attrs.ngDialogController,
controllerAs: attrs.ngDialogControllerAs,
bindToController: attrs.ngDialogBindToController,
disableAnimation: attrs.ngDialogDisableAnimation,
scope: ngDialogScope,
data: attrs.ngDialogData,
showClose: attrs.ngDialogShowClose === 'false' ? false : (attrs.ngDialogShowClose === 'true' ? true : defaults.showClose),
closeByDocument: attrs.ngDialogCloseByDocument === 'false' ? false : (attrs.ngDialogCloseByDocument === 'true' ? true : defaults.closeByDocument),
closeByEscape: attrs.ngDialogCloseByEscape === 'false' ? false : (attrs.ngDialogCloseByEscape === 'true' ? true : defaults.closeByEscape),
overlay: attrs.ngDialogOverlay === 'false' ? false : (attrs.ngDialogOverlay === 'true' ? true : defaults.overlay),
preCloseCallback: attrs.ngDialogPreCloseCallback || defaults.preCloseCallback,
onOpenCallback: attrs.ngDialogOnOpenCallback || defaults.onOpenCallback,
bodyClassName: attrs.ngDialogBodyClass || defaults.bodyClassName
});
});
}
};
}]);
return m;
}));
<article class="modalContainer alertModal">
<h2 class="modalHeader heading3">
{{ ngDialogData.modalHeader }}
</h2>
<div class="modalBody">
<div class="alertMessage">
{{ ngDialogData.modalBody }}
</div>
<div class="alertButtons">
<a class="button" ng-click="closeThisDialog(true)">{{ ngDialogData.trueOption }}</a>
<a class="link" ng-click="confirm(false)">{{ ngDialogData.falseOption }}</a>
</div>
</div>
</article>