| angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) |
| /** |
| * A helper, internal data structure that stores all references attached to key |
| */ |
| .factory('$$multiMap', function() { |
| return { |
| createNew: function() { |
| var map = {}; |
| |
| return { |
| entries: function() { |
| return Object.keys(map).map(function(key) { |
| return { |
| key: key, |
| value: map[key] |
| }; |
| }); |
| }, |
| get: function(key) { |
| return map[key]; |
| }, |
| hasKey: function(key) { |
| return !!map[key]; |
| }, |
| keys: function() { |
| return Object.keys(map); |
| }, |
| put: function(key, value) { |
| if (!map[key]) { |
| map[key] = []; |
| } |
| |
| map[key].push(value); |
| }, |
| remove: function(key, value) { |
| var values = map[key]; |
| |
| if (!values) { |
| return; |
| } |
| |
| var idx = values.indexOf(value); |
| |
| if (idx !== -1) { |
| values.splice(idx, 1); |
| } |
| |
| if (!values.length) { |
| delete map[key]; |
| } |
| } |
| }; |
| } |
| }; |
| }) |
| |
| /** |
| * A helper directive for the $modal service. It creates a backdrop element. |
| */ |
| .directive('uibModalBackdrop', [ |
| '$animate', '$injector', '$uibModalStack', |
| function($animate , $injector, $modalStack) { |
| var $animateCss = null; |
| |
| if ($injector.has('$animateCss')) { |
| $animateCss = $injector.get('$animateCss'); |
| } |
| |
| return { |
| replace: true, |
| templateUrl: 'template/modal/backdrop.html', |
| compile: function(tElement, tAttrs) { |
| tElement.addClass(tAttrs.backdropClass); |
| return linkFn; |
| } |
| }; |
| |
| function linkFn(scope, element, attrs) { |
| // Temporary fix for prefixing |
| element.addClass('modal-backdrop'); |
| |
| if (attrs.modalInClass) { |
| if ($animateCss) { |
| $animateCss(element, { |
| addClass: attrs.modalInClass |
| }).start(); |
| } else { |
| $animate.addClass(element, attrs.modalInClass); |
| } |
| |
| scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { |
| var done = setIsAsync(); |
| if ($animateCss) { |
| $animateCss(element, { |
| removeClass: attrs.modalInClass |
| }).start().then(done); |
| } else { |
| $animate.removeClass(element, attrs.modalInClass).then(done); |
| } |
| }); |
| } |
| } |
| }]) |
| |
| .directive('uibModalWindow', [ |
| '$uibModalStack', '$q', '$animate', '$injector', |
| function($modalStack , $q , $animate, $injector) { |
| var $animateCss = null; |
| |
| if ($injector.has('$animateCss')) { |
| $animateCss = $injector.get('$animateCss'); |
| } |
| |
| return { |
| scope: { |
| index: '@' |
| }, |
| replace: true, |
| transclude: true, |
| templateUrl: function(tElement, tAttrs) { |
| return tAttrs.templateUrl || 'template/modal/window.html'; |
| }, |
| link: function(scope, element, attrs) { |
| element.addClass(attrs.windowClass || ''); |
| element.addClass(attrs.windowTopClass || ''); |
| scope.size = attrs.size; |
| |
| scope.close = function(evt) { |
| var modal = $modalStack.getTop(); |
| if (modal && modal.value.backdrop && modal.value.backdrop !== 'static' && (evt.target === evt.currentTarget)) { |
| evt.preventDefault(); |
| evt.stopPropagation(); |
| $modalStack.dismiss(modal.key, 'backdrop click'); |
| } |
| }; |
| |
| // moved from template to fix issue #2280 |
| element.on('click', scope.close); |
| |
| // This property is only added to the scope for the purpose of detecting when this directive is rendered. |
| // We can detect that by using this property in the template associated with this directive and then use |
| // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. |
| scope.$isRendered = true; |
| |
| // Deferred object that will be resolved when this modal is render. |
| var modalRenderDeferObj = $q.defer(); |
| // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. |
| // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. |
| attrs.$observe('modalRender', function(value) { |
| if (value == 'true') { |
| modalRenderDeferObj.resolve(); |
| } |
| }); |
| |
| modalRenderDeferObj.promise.then(function() { |
| var animationPromise = null; |
| |
| if (attrs.modalInClass) { |
| if ($animateCss) { |
| animationPromise = $animateCss(element, { |
| addClass: attrs.modalInClass |
| }).start(); |
| } else { |
| animationPromise = $animate.addClass(element, attrs.modalInClass); |
| } |
| |
| scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { |
| var done = setIsAsync(); |
| if ($animateCss) { |
| $animateCss(element, { |
| removeClass: attrs.modalInClass |
| }).start().then(done); |
| } else { |
| $animate.removeClass(element, attrs.modalInClass).then(done); |
| } |
| }); |
| } |
| |
| |
| $q.when(animationPromise).then(function() { |
| var inputWithAutofocus = element[0].querySelector('[autofocus]'); |
| /** |
| * Auto-focusing of a freshly-opened modal element causes any child elements |
| * with the autofocus attribute to lose focus. This is an issue on touch |
| * based devices which will show and then hide the onscreen keyboard. |
| * Attempts to refocus the autofocus element via JavaScript will not reopen |
| * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus |
| * the modal element if the modal does not contain an autofocus element. |
| */ |
| if (inputWithAutofocus) { |
| inputWithAutofocus.focus(); |
| } else { |
| element[0].focus(); |
| } |
| }); |
| |
| // Notify {@link $modalStack} that modal is rendered. |
| var modal = $modalStack.getTop(); |
| if (modal) { |
| $modalStack.modalRendered(modal.key); |
| } |
| }); |
| } |
| }; |
| }]) |
| |
| .directive('uibModalAnimationClass', function() { |
| return { |
| compile: function(tElement, tAttrs) { |
| if (tAttrs.modalAnimation) { |
| tElement.addClass(tAttrs.uibModalAnimationClass); |
| } |
| } |
| }; |
| }) |
| |
| .directive('uibModalTransclude', function() { |
| return { |
| link: function($scope, $element, $attrs, controller, $transclude) { |
| $transclude($scope.$parent, function(clone) { |
| $element.empty(); |
| $element.append(clone); |
| }); |
| } |
| }; |
| }) |
| |
| .factory('$uibModalStack', [ |
| '$animate', '$timeout', '$document', '$compile', '$rootScope', |
| '$q', |
| '$injector', |
| '$$multiMap', |
| '$$stackedMap', |
| function($animate , $timeout , $document , $compile , $rootScope , |
| $q, |
| $injector, |
| $$multiMap, |
| $$stackedMap) { |
| var $animateCss = null; |
| |
| if ($injector.has('$animateCss')) { |
| $animateCss = $injector.get('$animateCss'); |
| } |
| |
| var OPENED_MODAL_CLASS = 'modal-open'; |
| |
| var backdropDomEl, backdropScope; |
| var openedWindows = $$stackedMap.createNew(); |
| var openedClasses = $$multiMap.createNew(); |
| var $modalStack = { |
| NOW_CLOSING_EVENT: 'modal.stack.now-closing' |
| }; |
| |
| //Modal focus behavior |
| var focusableElementList; |
| var focusIndex = 0; |
| var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' + |
| 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' + |
| 'iframe, object, embed, *[tabindex], *[contenteditable=true]'; |
| |
| function backdropIndex() { |
| var topBackdropIndex = -1; |
| var opened = openedWindows.keys(); |
| for (var i = 0; i < opened.length; i++) { |
| if (openedWindows.get(opened[i]).value.backdrop) { |
| topBackdropIndex = i; |
| } |
| } |
| return topBackdropIndex; |
| } |
| |
| $rootScope.$watch(backdropIndex, function(newBackdropIndex) { |
| if (backdropScope) { |
| backdropScope.index = newBackdropIndex; |
| } |
| }); |
| |
| function removeModalWindow(modalInstance, elementToReceiveFocus) { |
| var body = $document.find('body').eq(0); |
| var modalWindow = openedWindows.get(modalInstance).value; |
| |
| //clean up the stack |
| openedWindows.remove(modalInstance); |
| |
| removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { |
| var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; |
| openedClasses.remove(modalBodyClass, modalInstance); |
| body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass)); |
| toggleTopWindowClass(true); |
| }); |
| checkRemoveBackdrop(); |
| |
| //move focus to specified element if available, or else to body |
| if (elementToReceiveFocus && elementToReceiveFocus.focus) { |
| elementToReceiveFocus.focus(); |
| } else { |
| body.focus(); |
| } |
| } |
| |
| // Add or remove "windowTopClass" from the top window in the stack |
| function toggleTopWindowClass(toggleSwitch) { |
| var modalWindow; |
| |
| if (openedWindows.length() > 0) { |
| modalWindow = openedWindows.top().value; |
| modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch); |
| } |
| } |
| |
| function checkRemoveBackdrop() { |
| //remove backdrop if no longer needed |
| if (backdropDomEl && backdropIndex() == -1) { |
| var backdropScopeRef = backdropScope; |
| removeAfterAnimate(backdropDomEl, backdropScope, function() { |
| backdropScopeRef = null; |
| }); |
| backdropDomEl = undefined; |
| backdropScope = undefined; |
| } |
| } |
| |
| function removeAfterAnimate(domEl, scope, done) { |
| var asyncDeferred; |
| var asyncPromise = null; |
| var setIsAsync = function() { |
| if (!asyncDeferred) { |
| asyncDeferred = $q.defer(); |
| asyncPromise = asyncDeferred.promise; |
| } |
| |
| return function asyncDone() { |
| asyncDeferred.resolve(); |
| }; |
| }; |
| scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync); |
| |
| // Note that it's intentional that asyncPromise might be null. |
| // That's when setIsAsync has not been called during the |
| // NOW_CLOSING_EVENT broadcast. |
| return $q.when(asyncPromise).then(afterAnimating); |
| |
| function afterAnimating() { |
| if (afterAnimating.done) { |
| return; |
| } |
| afterAnimating.done = true; |
| |
| if ($animateCss) { |
| $animateCss(domEl, { |
| event: 'leave' |
| }).start().then(function() { |
| domEl.remove(); |
| }); |
| } else { |
| $animate.leave(domEl); |
| } |
| scope.$destroy(); |
| if (done) { |
| done(); |
| } |
| } |
| } |
| |
| $document.bind('keydown', function(evt) { |
| if (evt.isDefaultPrevented()) { |
| return evt; |
| } |
| |
| var modal = openedWindows.top(); |
| if (modal && modal.value.keyboard) { |
| switch (evt.which) { |
| case 27: { |
| evt.preventDefault(); |
| $rootScope.$apply(function() { |
| $modalStack.dismiss(modal.key, 'escape key press'); |
| }); |
| break; |
| } |
| case 9: { |
| $modalStack.loadFocusElementList(modal); |
| var focusChanged = false; |
| if (evt.shiftKey) { |
| if ($modalStack.isFocusInFirstItem(evt)) { |
| focusChanged = $modalStack.focusLastFocusableElement(); |
| } |
| } else { |
| if ($modalStack.isFocusInLastItem(evt)) { |
| focusChanged = $modalStack.focusFirstFocusableElement(); |
| } |
| } |
| |
| if (focusChanged) { |
| evt.preventDefault(); |
| evt.stopPropagation(); |
| } |
| break; |
| } |
| } |
| } |
| }); |
| |
| $modalStack.open = function(modalInstance, modal) { |
| var modalOpener = $document[0].activeElement, |
| modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; |
| |
| toggleTopWindowClass(false); |
| |
| openedWindows.add(modalInstance, { |
| deferred: modal.deferred, |
| renderDeferred: modal.renderDeferred, |
| modalScope: modal.scope, |
| backdrop: modal.backdrop, |
| keyboard: modal.keyboard, |
| openedClass: modal.openedClass, |
| windowTopClass: modal.windowTopClass |
| }); |
| |
| openedClasses.put(modalBodyClass, modalInstance); |
| |
| var body = $document.find('body').eq(0), |
| currBackdropIndex = backdropIndex(); |
| |
| if (currBackdropIndex >= 0 && !backdropDomEl) { |
| backdropScope = $rootScope.$new(true); |
| backdropScope.index = currBackdropIndex; |
| var angularBackgroundDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>'); |
| angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass); |
| if (modal.animation) { |
| angularBackgroundDomEl.attr('modal-animation', 'true'); |
| } |
| backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope); |
| body.append(backdropDomEl); |
| } |
| |
| var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>'); |
| angularDomEl.attr({ |
| 'template-url': modal.windowTemplateUrl, |
| 'window-class': modal.windowClass, |
| 'window-top-class': modal.windowTopClass, |
| 'size': modal.size, |
| 'index': openedWindows.length() - 1, |
| 'animate': 'animate' |
| }).html(modal.content); |
| if (modal.animation) { |
| angularDomEl.attr('modal-animation', 'true'); |
| } |
| |
| var modalDomEl = $compile(angularDomEl)(modal.scope); |
| openedWindows.top().value.modalDomEl = modalDomEl; |
| openedWindows.top().value.modalOpener = modalOpener; |
| body.append(modalDomEl); |
| body.addClass(modalBodyClass); |
| |
| $modalStack.clearFocusListCache(); |
| }; |
| |
| function broadcastClosing(modalWindow, resultOrReason, closing) { |
| return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; |
| } |
| |
| $modalStack.close = function(modalInstance, result) { |
| var modalWindow = openedWindows.get(modalInstance); |
| if (modalWindow && broadcastClosing(modalWindow, result, true)) { |
| modalWindow.value.modalScope.$$uibDestructionScheduled = true; |
| modalWindow.value.deferred.resolve(result); |
| removeModalWindow(modalInstance, modalWindow.value.modalOpener); |
| return true; |
| } |
| return !modalWindow; |
| }; |
| |
| $modalStack.dismiss = function(modalInstance, reason) { |
| var modalWindow = openedWindows.get(modalInstance); |
| if (modalWindow && broadcastClosing(modalWindow, reason, false)) { |
| modalWindow.value.modalScope.$$uibDestructionScheduled = true; |
| modalWindow.value.deferred.reject(reason); |
| removeModalWindow(modalInstance, modalWindow.value.modalOpener); |
| return true; |
| } |
| return !modalWindow; |
| }; |
| |
| $modalStack.dismissAll = function(reason) { |
| var topModal = this.getTop(); |
| while (topModal && this.dismiss(topModal.key, reason)) { |
| topModal = this.getTop(); |
| } |
| }; |
| |
| $modalStack.getTop = function() { |
| return openedWindows.top(); |
| }; |
| |
| $modalStack.modalRendered = function(modalInstance) { |
| var modalWindow = openedWindows.get(modalInstance); |
| if (modalWindow) { |
| modalWindow.value.renderDeferred.resolve(); |
| } |
| }; |
| |
| $modalStack.focusFirstFocusableElement = function() { |
| if (focusableElementList.length > 0) { |
| focusableElementList[0].focus(); |
| return true; |
| } |
| return false; |
| }; |
| $modalStack.focusLastFocusableElement = function() { |
| if (focusableElementList.length > 0) { |
| focusableElementList[focusableElementList.length - 1].focus(); |
| return true; |
| } |
| return false; |
| }; |
| |
| $modalStack.isFocusInFirstItem = function(evt) { |
| if (focusableElementList.length > 0) { |
| return (evt.target || evt.srcElement) == focusableElementList[0]; |
| } |
| return false; |
| }; |
| |
| $modalStack.isFocusInLastItem = function(evt) { |
| if (focusableElementList.length > 0) { |
| return (evt.target || evt.srcElement) == focusableElementList[focusableElementList.length - 1]; |
| } |
| return false; |
| }; |
| |
| $modalStack.clearFocusListCache = function() { |
| focusableElementList = []; |
| focusIndex = 0; |
| }; |
| |
| $modalStack.loadFocusElementList = function(modalWindow) { |
| if (focusableElementList === undefined || !focusableElementList.length) { |
| if (modalWindow) { |
| var modalDomE1 = modalWindow.value.modalDomEl; |
| if (modalDomE1 && modalDomE1.length) { |
| focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector); |
| } |
| } |
| } |
| }; |
| |
| return $modalStack; |
| }]) |
| |
| .provider('$uibModal', function() { |
| var $modalProvider = { |
| options: { |
| animation: true, |
| backdrop: true, //can also be false or 'static' |
| keyboard: true |
| }, |
| $get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$uibModalStack', '$modalSuppressWarning', '$log', |
| function ($injector, $rootScope, $q, $templateRequest, $controller, $modalStack, $modalSuppressWarning, $log) { |
| var $modal = {}; |
| |
| function getTemplatePromise(options) { |
| return options.template ? $q.when(options.template) : |
| $templateRequest(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl); |
| } |
| |
| function getResolvePromises(resolves) { |
| var promisesArr = []; |
| angular.forEach(resolves, function(value) { |
| if (angular.isFunction(value) || angular.isArray(value)) { |
| promisesArr.push($q.when($injector.invoke(value))); |
| } else if (angular.isString(value)) { |
| promisesArr.push($q.when($injector.get(value))); |
| } else { |
| promisesArr.push($q.when(value)); |
| } |
| }); |
| return promisesArr; |
| } |
| |
| var promiseChain = null; |
| $modal.getPromiseChain = function() { |
| return promiseChain; |
| }; |
| |
| $modal.open = function(modalOptions) { |
| var modalResultDeferred = $q.defer(); |
| var modalOpenedDeferred = $q.defer(); |
| var modalRenderDeferred = $q.defer(); |
| |
| //prepare an instance of a modal to be injected into controllers and returned to a caller |
| var modalInstance = { |
| result: modalResultDeferred.promise, |
| opened: modalOpenedDeferred.promise, |
| rendered: modalRenderDeferred.promise, |
| close: function (result) { |
| return $modalStack.close(modalInstance, result); |
| }, |
| dismiss: function (reason) { |
| return $modalStack.dismiss(modalInstance, reason); |
| } |
| }; |
| |
| //merge and clean up options |
| modalOptions = angular.extend({}, $modalProvider.options, modalOptions); |
| modalOptions.resolve = modalOptions.resolve || {}; |
| |
| //verify options |
| if (!modalOptions.template && !modalOptions.templateUrl) { |
| throw new Error('One of template or templateUrl options is required.'); |
| } |
| |
| var templateAndResolvePromise = |
| $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve))); |
| |
| function resolveWithTemplate() { |
| return templateAndResolvePromise; |
| } |
| |
| // Wait for the resolution of the existing promise chain. |
| // Then switch to our own combined promise dependency (regardless of how the previous modal fared). |
| // Then add to $modalStack and resolve opened. |
| // Finally clean up the chain variable if no subsequent modal has overwritten it. |
| var samePromise; |
| samePromise = promiseChain = $q.all([promiseChain]) |
| .then(resolveWithTemplate, resolveWithTemplate) |
| .then(function resolveSuccess(tplAndVars) { |
| |
| var modalScope = (modalOptions.scope || $rootScope).$new(); |
| modalScope.$close = modalInstance.close; |
| modalScope.$dismiss = modalInstance.dismiss; |
| |
| modalScope.$on('$destroy', function() { |
| if (!modalScope.$$uibDestructionScheduled) { |
| modalScope.$dismiss('$uibUnscheduledDestruction'); |
| } |
| }); |
| |
| var ctrlInstance, ctrlLocals = {}; |
| var resolveIter = 1; |
| |
| //controllers |
| if (modalOptions.controller) { |
| ctrlLocals.$scope = modalScope; |
| ctrlLocals.$uibModalInstance = modalInstance; |
| Object.defineProperty(ctrlLocals, '$modalInstance', { |
| get: function() { |
| if (!$modalSuppressWarning) { |
| $log.warn('$modalInstance is now deprecated. Use $uibModalInstance instead.'); |
| } |
| |
| return modalInstance; |
| } |
| }); |
| angular.forEach(modalOptions.resolve, function(value, key) { |
| ctrlLocals[key] = tplAndVars[resolveIter++]; |
| }); |
| |
| ctrlInstance = $controller(modalOptions.controller, ctrlLocals); |
| if (modalOptions.controllerAs) { |
| if (modalOptions.bindToController) { |
| angular.extend(ctrlInstance, modalScope); |
| } |
| |
| modalScope[modalOptions.controllerAs] = ctrlInstance; |
| } |
| } |
| |
| $modalStack.open(modalInstance, { |
| scope: modalScope, |
| deferred: modalResultDeferred, |
| renderDeferred: modalRenderDeferred, |
| content: tplAndVars[0], |
| animation: modalOptions.animation, |
| backdrop: modalOptions.backdrop, |
| keyboard: modalOptions.keyboard, |
| backdropClass: modalOptions.backdropClass, |
| windowTopClass: modalOptions.windowTopClass, |
| windowClass: modalOptions.windowClass, |
| windowTemplateUrl: modalOptions.windowTemplateUrl, |
| size: modalOptions.size, |
| openedClass: modalOptions.openedClass |
| }); |
| modalOpenedDeferred.resolve(true); |
| |
| }, function resolveError(reason) { |
| modalOpenedDeferred.reject(reason); |
| modalResultDeferred.reject(reason); |
| }) |
| .finally(function() { |
| if (promiseChain === samePromise) { |
| promiseChain = null; |
| } |
| }); |
| |
| return modalInstance; |
| }; |
| |
| return $modal; |
| } |
| ] |
| }; |
| |
| return $modalProvider; |
| }); |
| |
| /* deprecated modal below */ |
| |
| angular.module('ui.bootstrap.modal') |
| |
| .value('$modalSuppressWarning', false) |
| |
| /** |
| * A helper directive for the $modal service. It creates a backdrop element. |
| */ |
| .directive('modalBackdrop', [ |
| '$animate', '$injector', '$modalStack', '$log', '$modalSuppressWarning', |
| function($animate , $injector, $modalStack, $log, $modalSuppressWarning) { |
| var $animateCss = null; |
| |
| if ($injector.has('$animateCss')) { |
| $animateCss = $injector.get('$animateCss'); |
| } |
| |
| return { |
| replace: true, |
| templateUrl: 'template/modal/backdrop.html', |
| compile: function(tElement, tAttrs) { |
| tElement.addClass(tAttrs.backdropClass); |
| return linkFn; |
| } |
| }; |
| |
| function linkFn(scope, element, attrs) { |
| if (!$modalSuppressWarning) { |
| $log.warn('modal-backdrop is now deprecated. Use uib-modal-backdrop instead.'); |
| } |
| element.addClass('modal-backdrop'); |
| |
| if (attrs.modalInClass) { |
| if ($animateCss) { |
| $animateCss(element, { |
| addClass: attrs.modalInClass |
| }).start(); |
| } else { |
| $animate.addClass(element, attrs.modalInClass); |
| } |
| |
| scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { |
| var done = setIsAsync(); |
| if ($animateCss) { |
| $animateCss(element, { |
| removeClass: attrs.modalInClass |
| }).start().then(done); |
| } else { |
| $animate.removeClass(element, attrs.modalInClass).then(done); |
| } |
| }); |
| } |
| } |
| }]) |
| |
| .directive('modalWindow', [ |
| '$modalStack', '$q', '$animate', '$injector', '$log', '$modalSuppressWarning', |
| function($modalStack , $q , $animate, $injector, $log, $modalSuppressWarning) { |
| var $animateCss = null; |
| |
| if ($injector.has('$animateCss')) { |
| $animateCss = $injector.get('$animateCss'); |
| } |
| |
| return { |
| scope: { |
| index: '@' |
| }, |
| replace: true, |
| transclude: true, |
| templateUrl: function(tElement, tAttrs) { |
| return tAttrs.templateUrl || 'template/modal/window.html'; |
| }, |
| link: function(scope, element, attrs) { |
| if (!$modalSuppressWarning) { |
| $log.warn('modal-window is now deprecated. Use uib-modal-window instead.'); |
| } |
| element.addClass(attrs.windowClass || ''); |
| element.addClass(attrs.windowTopClass || ''); |
| scope.size = attrs.size; |
| |
| scope.close = function(evt) { |
| var modal = $modalStack.getTop(); |
| if (modal && modal.value.backdrop && modal.value.backdrop !== 'static' && (evt.target === evt.currentTarget)) { |
| evt.preventDefault(); |
| evt.stopPropagation(); |
| $modalStack.dismiss(modal.key, 'backdrop click'); |
| } |
| }; |
| |
| // moved from template to fix issue #2280 |
| element.on('click', scope.close); |
| |
| // This property is only added to the scope for the purpose of detecting when this directive is rendered. |
| // We can detect that by using this property in the template associated with this directive and then use |
| // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. |
| scope.$isRendered = true; |
| |
| // Deferred object that will be resolved when this modal is render. |
| var modalRenderDeferObj = $q.defer(); |
| // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. |
| // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. |
| attrs.$observe('modalRender', function(value) { |
| if (value == 'true') { |
| modalRenderDeferObj.resolve(); |
| } |
| }); |
| |
| modalRenderDeferObj.promise.then(function() { |
| var animationPromise = null; |
| |
| if (attrs.modalInClass) { |
| if ($animateCss) { |
| animationPromise = $animateCss(element, { |
| addClass: attrs.modalInClass |
| }).start(); |
| } else { |
| animationPromise = $animate.addClass(element, attrs.modalInClass); |
| } |
| |
| scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { |
| var done = setIsAsync(); |
| if ($animateCss) { |
| $animateCss(element, { |
| removeClass: attrs.modalInClass |
| }).start().then(done); |
| } else { |
| $animate.removeClass(element, attrs.modalInClass).then(done); |
| } |
| }); |
| } |
| |
| |
| $q.when(animationPromise).then(function() { |
| var inputWithAutofocus = element[0].querySelector('[autofocus]'); |
| /** |
| * Auto-focusing of a freshly-opened modal element causes any child elements |
| * with the autofocus attribute to lose focus. This is an issue on touch |
| * based devices which will show and then hide the onscreen keyboard. |
| * Attempts to refocus the autofocus element via JavaScript will not reopen |
| * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus |
| * the modal element if the modal does not contain an autofocus element. |
| */ |
| if (inputWithAutofocus) { |
| inputWithAutofocus.focus(); |
| } else { |
| element[0].focus(); |
| } |
| }); |
| |
| // Notify {@link $modalStack} that modal is rendered. |
| var modal = $modalStack.getTop(); |
| if (modal) { |
| $modalStack.modalRendered(modal.key); |
| } |
| }); |
| } |
| }; |
| }]) |
| |
| .directive('modalAnimationClass', [ |
| '$log', '$modalSuppressWarning', |
| function ($log, $modalSuppressWarning) { |
| return { |
| compile: function(tElement, tAttrs) { |
| if (!$modalSuppressWarning) { |
| $log.warn('modal-animation-class is now deprecated. Use uib-modal-animation-class instead.'); |
| } |
| if (tAttrs.modalAnimation) { |
| tElement.addClass(tAttrs.modalAnimationClass); |
| } |
| } |
| }; |
| }]) |
| |
| .directive('modalTransclude', [ |
| '$log', '$modalSuppressWarning', |
| function ($log, $modalSuppressWarning) { |
| return { |
| link: function($scope, $element, $attrs, controller, $transclude) { |
| if (!$modalSuppressWarning) { |
| $log.warn('modal-transclude is now deprecated. Use uib-modal-transclude instead.'); |
| } |
| $transclude($scope.$parent, function(clone) { |
| $element.empty(); |
| $element.append(clone); |
| }); |
| } |
| }; |
| }]) |
| |
| .service('$modalStack', [ |
| '$animate', '$timeout', '$document', '$compile', '$rootScope', |
| '$q', |
| '$injector', |
| '$$multiMap', |
| '$$stackedMap', |
| '$uibModalStack', |
| '$log', |
| '$modalSuppressWarning', |
| function($animate , $timeout , $document , $compile , $rootScope , |
| $q, |
| $injector, |
| $$multiMap, |
| $$stackedMap, |
| $uibModalStack, |
| $log, |
| $modalSuppressWarning) { |
| if (!$modalSuppressWarning) { |
| $log.warn('$modalStack is now deprecated. Use $uibModalStack instead.'); |
| } |
| |
| angular.extend(this, $uibModalStack); |
| }]) |
| |
| .provider('$modal', ['$uibModalProvider', function($uibModalProvider) { |
| angular.extend(this, $uibModalProvider); |
| |
| this.$get = ['$injector', '$log', '$modalSuppressWarning', |
| function ($injector, $log, $modalSuppressWarning) { |
| if (!$modalSuppressWarning) { |
| $log.warn('$modal is now deprecated. Use $uibModal instead.'); |
| } |
| |
| return $injector.invoke($uibModalProvider.$get); |
| }]; |
| }]); |