modal.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /**
  2. * angular-strap
  3. * @version v2.3.5 - 2015-10-29
  4. * @link http://mgcrea.github.io/angular-strap
  5. * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
  6. * @license MIT License, http://www.opensource.org/licenses/MIT
  7. */
  8. 'use strict';
  9. angular.module('mgcrea.ngStrap.modal', [ 'mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$modal', function() {
  10. var defaults = this.defaults = {
  11. animation: 'am-fade',
  12. backdropAnimation: 'am-fade',
  13. prefixClass: 'modal',
  14. prefixEvent: 'modal',
  15. placement: 'top',
  16. templateUrl: 'modal/modal.tpl.html',
  17. template: '',
  18. contentTemplate: false,
  19. container: false,
  20. element: null,
  21. backdrop: true,
  22. keyboard: true,
  23. html: false,
  24. show: true
  25. };
  26. this.$get = [ '$window', '$rootScope', '$bsCompiler', '$animate', '$timeout', '$sce', 'dimensions', function($window, $rootScope, $bsCompiler, $animate, $timeout, $sce, dimensions) {
  27. var forEach = angular.forEach;
  28. var trim = String.prototype.trim;
  29. var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
  30. var bodyElement = angular.element($window.document.body);
  31. function ModalFactory(config) {
  32. var $modal = {};
  33. var options = $modal.$options = angular.extend({}, defaults, config);
  34. var promise = $modal.$promise = $bsCompiler.compile(options);
  35. var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();
  36. if (!options.element && !options.container) {
  37. options.container = 'body';
  38. }
  39. $modal.$id = options.id || options.element && options.element.attr('id') || '';
  40. forEach([ 'title', 'content' ], function(key) {
  41. if (options[key]) scope[key] = $sce.trustAsHtml(options[key]);
  42. });
  43. scope.$hide = function() {
  44. scope.$$postDigest(function() {
  45. $modal.hide();
  46. });
  47. };
  48. scope.$show = function() {
  49. scope.$$postDigest(function() {
  50. $modal.show();
  51. });
  52. };
  53. scope.$toggle = function() {
  54. scope.$$postDigest(function() {
  55. $modal.toggle();
  56. });
  57. };
  58. $modal.$isShown = scope.$isShown = false;
  59. var compileData, modalElement, modalScope;
  60. var backdropElement = angular.element('<div class="' + options.prefixClass + '-backdrop"/>');
  61. backdropElement.css({
  62. position: 'fixed',
  63. top: '0px',
  64. left: '0px',
  65. bottom: '0px',
  66. right: '0px',
  67. 'z-index': 1038
  68. });
  69. promise.then(function(data) {
  70. compileData = data;
  71. $modal.init();
  72. });
  73. $modal.init = function() {
  74. if (options.show) {
  75. scope.$$postDigest(function() {
  76. $modal.show();
  77. });
  78. }
  79. };
  80. $modal.destroy = function() {
  81. destroyModalElement();
  82. if (backdropElement) {
  83. backdropElement.remove();
  84. backdropElement = null;
  85. }
  86. scope.$destroy();
  87. };
  88. $modal.show = function() {
  89. if ($modal.$isShown) return;
  90. var parent, after;
  91. if (angular.isElement(options.container)) {
  92. parent = options.container;
  93. after = options.container[0].lastChild ? angular.element(options.container[0].lastChild) : null;
  94. } else {
  95. if (options.container) {
  96. parent = findElement(options.container);
  97. after = parent[0] && parent[0].lastChild ? angular.element(parent[0].lastChild) : null;
  98. } else {
  99. parent = null;
  100. after = options.element;
  101. }
  102. }
  103. if (modalElement) destroyModalElement();
  104. modalScope = $modal.$scope.$new();
  105. modalElement = $modal.$element = compileData.link(modalScope, function(clonedElement, scope) {});
  106. if (scope.$emit(options.prefixEvent + '.show.before', $modal).defaultPrevented) {
  107. return;
  108. }
  109. modalElement.css({
  110. display: 'block'
  111. }).addClass(options.placement);
  112. if (options.animation) {
  113. if (options.backdrop) {
  114. backdropElement.addClass(options.backdropAnimation);
  115. }
  116. modalElement.addClass(options.animation);
  117. }
  118. if (options.backdrop) {
  119. $animate.enter(backdropElement, bodyElement, null);
  120. }
  121. if (angular.version.minor <= 2) {
  122. $animate.enter(modalElement, parent, after, enterAnimateCallback);
  123. } else {
  124. $animate.enter(modalElement, parent, after).then(enterAnimateCallback);
  125. }
  126. $modal.$isShown = scope.$isShown = true;
  127. safeDigest(scope);
  128. var el = modalElement[0];
  129. requestAnimationFrame(function() {
  130. el.focus();
  131. });
  132. bodyElement.addClass(options.prefixClass + '-open');
  133. if (options.animation) {
  134. bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
  135. }
  136. bindBackdropEvents();
  137. bindKeyboardEvents();
  138. };
  139. function enterAnimateCallback() {
  140. scope.$emit(options.prefixEvent + '.show', $modal);
  141. }
  142. $modal.hide = function() {
  143. if (!$modal.$isShown) return;
  144. if (scope.$emit(options.prefixEvent + '.hide.before', $modal).defaultPrevented) {
  145. return;
  146. }
  147. if (angular.version.minor <= 2) {
  148. $animate.leave(modalElement, leaveAnimateCallback);
  149. } else {
  150. $animate.leave(modalElement).then(leaveAnimateCallback);
  151. }
  152. if (options.backdrop) {
  153. $animate.leave(backdropElement);
  154. }
  155. $modal.$isShown = scope.$isShown = false;
  156. safeDigest(scope);
  157. unbindBackdropEvents();
  158. unbindKeyboardEvents();
  159. };
  160. function leaveAnimateCallback() {
  161. scope.$emit(options.prefixEvent + '.hide', $modal);
  162. bodyElement.removeClass(options.prefixClass + '-open');
  163. if (options.animation) {
  164. bodyElement.removeClass(options.prefixClass + '-with-' + options.animation);
  165. }
  166. }
  167. $modal.toggle = function() {
  168. $modal.$isShown ? $modal.hide() : $modal.show();
  169. };
  170. $modal.focus = function() {
  171. modalElement[0].focus();
  172. };
  173. $modal.$onKeyUp = function(evt) {
  174. if (evt.which === 27 && $modal.$isShown) {
  175. $modal.hide();
  176. evt.stopPropagation();
  177. }
  178. };
  179. function bindBackdropEvents() {
  180. if (options.backdrop) {
  181. modalElement.on('click', hideOnBackdropClick);
  182. backdropElement.on('click', hideOnBackdropClick);
  183. backdropElement.on('wheel', preventEventDefault);
  184. }
  185. }
  186. function unbindBackdropEvents() {
  187. if (options.backdrop) {
  188. modalElement.off('click', hideOnBackdropClick);
  189. backdropElement.off('click', hideOnBackdropClick);
  190. backdropElement.off('wheel', preventEventDefault);
  191. }
  192. }
  193. function bindKeyboardEvents() {
  194. if (options.keyboard) {
  195. modalElement.on('keyup', $modal.$onKeyUp);
  196. }
  197. }
  198. function unbindKeyboardEvents() {
  199. if (options.keyboard) {
  200. modalElement.off('keyup', $modal.$onKeyUp);
  201. }
  202. }
  203. function hideOnBackdropClick(evt) {
  204. if (evt.target !== evt.currentTarget) return;
  205. options.backdrop === 'static' ? $modal.focus() : $modal.hide();
  206. }
  207. function preventEventDefault(evt) {
  208. evt.preventDefault();
  209. }
  210. function destroyModalElement() {
  211. if ($modal.$isShown && modalElement !== null) {
  212. unbindBackdropEvents();
  213. unbindKeyboardEvents();
  214. }
  215. if (modalScope) {
  216. modalScope.$destroy();
  217. modalScope = null;
  218. }
  219. if (modalElement) {
  220. modalElement.remove();
  221. modalElement = $modal.$element = null;
  222. }
  223. }
  224. return $modal;
  225. }
  226. function safeDigest(scope) {
  227. scope.$$phase || scope.$root && scope.$root.$$phase || scope.$digest();
  228. }
  229. function findElement(query, element) {
  230. return angular.element((element || document).querySelectorAll(query));
  231. }
  232. return ModalFactory;
  233. } ];
  234. }).directive('bsModal', [ '$window', '$sce', '$modal', function($window, $sce, $modal) {
  235. return {
  236. restrict: 'EAC',
  237. scope: true,
  238. link: function postLink(scope, element, attr, transclusion) {
  239. var options = {
  240. scope: scope,
  241. element: element,
  242. show: false
  243. };
  244. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation', 'backdropAnimation', 'id', 'prefixEvent', 'prefixClass' ], function(key) {
  245. if (angular.isDefined(attr[key])) options[key] = attr[key];
  246. });
  247. var falseValueRegExp = /^(false|0|)$/i;
  248. angular.forEach([ 'backdrop', 'keyboard', 'html', 'container' ], function(key) {
  249. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  250. });
  251. angular.forEach([ 'title', 'content' ], function(key) {
  252. attr[key] && attr.$observe(key, function(newValue, oldValue) {
  253. scope[key] = $sce.trustAsHtml(newValue);
  254. });
  255. });
  256. attr.bsModal && scope.$watch(attr.bsModal, function(newValue, oldValue) {
  257. if (angular.isObject(newValue)) {
  258. angular.extend(scope, newValue);
  259. } else {
  260. scope.content = newValue;
  261. }
  262. }, true);
  263. var modal = $modal(options);
  264. element.on(attr.trigger || 'click', modal.toggle);
  265. scope.$on('$destroy', function() {
  266. if (modal) modal.destroy();
  267. options = null;
  268. modal = null;
  269. });
  270. }
  271. };
  272. } ]);