diff options
Diffstat (limited to 'libs/bower_components/ngDialog/js/ngDialog.js')
-rw-r--r-- | libs/bower_components/ngDialog/js/ngDialog.js | 1291 |
1 files changed, 948 insertions, 343 deletions
diff --git a/libs/bower_components/ngDialog/js/ngDialog.js b/libs/bower_components/ngDialog/js/ngDialog.js index 5c393901e8..ddd287c142 100644 --- a/libs/bower_components/ngDialog/js/ngDialog.js +++ b/libs/bower_components/ngDialog/js/ngDialog.js @@ -1,348 +1,953 @@ /* * ngDialog - easy modals and popup windows * http://github.com/likeastore/ngDialog - * (c) 2013 MIT License, https://likeastore.com + * (c) 2013-2015 MIT License, https://likeastore.com */ -(function (window, angular, undefined) { - 'use strict'; - - var module = 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 forceBodyReload = false; - - module.provider('ngDialog', function () { - var defaults = this.defaults = { - className: 'ngdialog-theme-default', - plain: false, - showClose: true, - closeByDocument: true, - closeByEscape: true, - appendTo: false - }; - - this.setForceBodyReload = function (_useIt) { - forceBodyReload = _useIt || false; - }; - - this.setDefaults = function (newDefaults) { - angular.extend(defaults, newDefaults); - }; - - var globalID = 0, dialogsCount = 0, closeByDocumentHandler, defers = {}; - - this.$get = ['$document', '$templateCache', '$compile', '$q', '$http', '$rootScope', '$timeout', '$window', '$controller', - function ($document, $templateCache, $compile, $q, $http, $rootScope, $timeout, $window, $controller) { - var $body = $document.find('body'); - if (forceBodyReload) { - $rootScope.$on('$locationChangeSuccess', function () { - $body = $document.find('body'); - }); - } - - var privateMethods = { - onDocumentKeydown: function (event) { - if (event.keyCode === 27) { - publicMethods.close('$escape'); - } - }, - - setBodyPadding: function (width) { - var originalBodyPadding = parseInt(($body.css('padding-right') || 0), 10); - $body.css('padding-right', (originalBodyPadding + width) + 'px'); - $body.data('ng-dialog-original-padding', originalBodyPadding); - }, - - resetBodyPadding: function () { - var originalBodyPadding = $body.data('ng-dialog-original-padding'); - if (originalBodyPadding) { - $body.css('padding-right', originalBodyPadding + 'px'); - } else { - $body.css('padding-right', ''); - } - }, - - closeDialog: function ($dialog, value) { - var id = $dialog.attr('id'); - if (typeof window.Hammer !== 'undefined') { - window.Hammer($dialog[0]).off('tap', closeByDocumentHandler); - } else { - $dialog.unbind('click'); - } - - if (dialogsCount === 1) { - $body.unbind('keydown'); - } - - if (!$dialog.hasClass("ngdialog-closing")){ - dialogsCount -= 1; - } - - if (animationEndSupport) { - $dialog.unbind(animationEndEvent).bind(animationEndEvent, function () { - $dialog.scope().$destroy(); - $dialog.remove(); - if (dialogsCount === 0) { - $body.removeClass('ngdialog-open'); - privateMethods.resetBodyPadding(); - } - $rootScope.$broadcast('ngDialog.closed', $dialog); - }).addClass('ngdialog-closing'); - } else { - $dialog.scope().$destroy(); - $dialog.remove(); - if (dialogsCount === 0) { - $body.removeClass('ngdialog-open'); - privateMethods.resetBodyPadding(); - } - $rootScope.$broadcast('ngDialog.closed', $dialog); - } - if (defers[id]) { - defers[id].resolve({ - id: id, - value: value, - $dialog: $dialog, - remainingDialogs: dialogsCount - }); - delete defers[id]; - } - } - }; - - var publicMethods = { - - /* - * @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} - * - className {String} - dialog theme class - * - showClose {Boolean} - show close button, default true - * - closeByEscape {Boolean} - default true - * - closeByDocument {Boolean} - default true - * - * @return {Object} dialog - */ - open: function (opts) { - var self = this; - var options = angular.copy(defaults); - - opts = opts || {}; - angular.extend(options, opts); - - globalID += 1; - - self.latestID = 'ngdialog' + globalID; - - var defer; - defers[self.latestID] = defer = $q.defer(); - - var scope = angular.isObject(options.scope) ? options.scope.$new() : $rootScope.$new(); - var $dialog, $dialogParent; - - $q.when(loadTemplate(options.template)).then(function (template) { - template = angular.isString(template) ? - template : - template.data && angular.isString(template.data) ? - template.data : - ''; - - $templateCache.put(options.template, template); - - if (options.showClose) { - template += '<div class="ngdialog-close"></div>'; - } - - self.$result = $dialog = $el('<div id="ngdialog' + globalID + '" class="ngdialog"></div>'); - $dialog.html('<div class="ngdialog-overlay"></div><div class="ngdialog-content">' + template + '</div>'); - - if (options.data && angular.isString(options.data)) { - var firstLetter = options.data.replace(/^\s*/, '')[0]; - scope.ngDialogData = (firstLetter === '{' || firstLetter === '[') ? angular.fromJson(options.data) : options.data; - } else if (options.data && angular.isObject(options.data)) { - scope.ngDialogData = angular.fromJson(angular.toJson(options.data)); - } - - if (options.controller && (angular.isString(options.controller) || angular.isArray(options.controller) || angular.isFunction(options.controller))) { - var controllerInstance = $controller(options.controller, { - $scope: scope, - $element: $dialog - }); - $dialog.data('$ngDialogControllerController', controllerInstance); - } - - if (options.className) { - $dialog.addClass(options.className); - } - - if (options.appendTo && angular.isString(options.appendTo)) { - $dialogParent = angular.element(document.querySelector(options.appendTo)); - } else { - $dialogParent = $body; - } - - scope.closeThisDialog = function (value) { - privateMethods.closeDialog($dialog, value); - }; - - $timeout(function () { - $compile($dialog)(scope); - - var widthDiffs = $window.innerWidth - $body.prop('clientWidth'); - $body.addClass('ngdialog-open'); - var scrollBarWidth = widthDiffs - ($window.innerWidth - $body.prop('clientWidth')); - if (scrollBarWidth > 0) { - privateMethods.setBodyPadding(scrollBarWidth); - } - $dialogParent.append($dialog); - $rootScope.$broadcast('ngDialog.opened', $dialog); - }); - - if (options.closeByEscape) { - $body.bind('keydown', privateMethods.onDocumentKeydown); - } - - 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') { - window.Hammer($dialog[0]).on('tap', closeByDocumentHandler); - } else { - $dialog.bind('click', closeByDocumentHandler); - } - - dialogsCount += 1; - - return publicMethods; - }); - - return { - id: 'ngdialog' + globalID, - closePromise: defer.promise, - close: function(value) { - privateMethods.closeDialog($dialog, value); - } - }; - - function loadTemplate (tmpl) { - if (!tmpl) { - return 'Empty template'; - } - - if (angular.isString(tmpl) && options.plain) { - return tmpl; - } - - return $templateCache.get(tmpl) || $http.get(tmpl, { cache: true }); - } - }, - - /* - * @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} - * - className {String} - dialog theme class - * - showClose {Boolean} - show close button, default true - * - closeByEscape {Boolean} - default false - * - closeByDocument {Boolean} - default false - * - * @return {Object} dialog - */ - openConfirm: function (opts) { - var defer = $q.defer(); - - var options = { - closeByEscape: false, - closeByDocument: false - }; - angular.extend(options, opts); - - options.scope = angular.isObject(options.scope) ? options.scope.$new() : $rootScope.$new(); - options.scope.confirm = function (value) { - defer.resolve(value); - openResult.close(value); - }; - - var openResult = publicMethods.open(options); - openResult.closePromise.then(function (data) { - if (data) { - return defer.reject(data.value); - } - return defer.reject(); - }); - - return defer.promise; - }, - - /* - * @param {String} id - * @return {Object} dialog - */ - close: function (id, value) { - var $dialog = $el(document.getElementById(id)); - - if ($dialog.length) { - privateMethods.closeDialog($dialog, value); - } else { - publicMethods.closeAll(value); - } - - return publicMethods; - }, - - closeAll: function (value) { - var $all = document.querySelectorAll('.ngdialog'); - - angular.forEach($all, function (dialog) { - privateMethods.closeDialog($el(dialog), value); - }); - } - }; - - return publicMethods; - }]; - }); - - module.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); - - ngDialog.open({ - template: attrs.ngDialog, - className: attrs.ngDialogClass, - controller: attrs.ngDialogController, - scope: ngDialogScope , - data: attrs.ngDialogData, - showClose: attrs.ngDialogShowClose === 'false' ? false : true, - closeByDocument: attrs.ngDialogCloseByDocument === 'false' ? false : true, - closeByEscape: attrs.ngDialogCloseByEscape === 'false' ? false : true - }); - }); - } - }; - }]); - -})(window, window.angular); +(function (root, factory) { + 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 += '<button aria-label="Dismiss" class="ngdialog-close"></button>'; + } + + 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; +})); |