You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

478 lines
13 KiB

  1. /*
  2. * # Semantic - Modal
  3. * http://github.com/jlukic/semantic-ui/
  4. *
  5. *
  6. * Copyright 2013 Contributors
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. $.fn.modal = function(parameters) {
  13. var
  14. $allModals = $(this),
  15. $document = $(document),
  16. settings = ( $.isPlainObject(parameters) )
  17. ? $.extend(true, {}, $.fn.modal.settings, parameters)
  18. : $.fn.modal.settings,
  19. selector = settings.selector,
  20. className = settings.className,
  21. namespace = settings.namespace,
  22. error = settings.error,
  23. eventNamespace = '.' + namespace,
  24. moduleNamespace = 'module-' + namespace,
  25. moduleSelector = $allModals.selector || '',
  26. time = new Date().getTime(),
  27. performance = [],
  28. query = arguments[0],
  29. methodInvoked = (typeof query == 'string'),
  30. queryArguments = [].slice.call(arguments, 1),
  31. invokedResponse
  32. ;
  33. $allModals
  34. .each(function() {
  35. var
  36. $modal = $(this),
  37. $context = $(settings.context),
  38. $otherModals = $allModals.not($modal),
  39. $closeButton = $modal.find(selector.closeButton),
  40. $dimmer,
  41. element = this,
  42. instance = $modal.data(moduleNamespace),
  43. modal
  44. ;
  45. modal = {
  46. initialize: function() {
  47. modal.verbose('Attaching events');
  48. $closeButton
  49. .on('click', modal.event.close)
  50. ;
  51. modal.cache.sizes();
  52. modal.verbose('Creating dimmer');
  53. $context
  54. .dimmer({
  55. closable: settings.closable,
  56. duration: settings.duration,
  57. onShow: function() {
  58. modal.add.keyboardShortcuts();
  59. $.proxy(settings.onShow, this)();
  60. },
  61. onHide: function() {
  62. if($modal.is(':visible')) {
  63. $context.off('.dimmer');
  64. modal.hide();
  65. $.proxy(settings.onHide, this)();
  66. }
  67. modal.remove.keyboardShortcuts();
  68. }
  69. })
  70. ;
  71. $dimmer = $context.children(selector.dimmer);
  72. if( $modal.parent()[0] !== $dimmer[0] ) {
  73. modal.debug('Moving element inside dimmer', $context);
  74. $modal = $modal
  75. .detach()
  76. .appendTo($dimmer)
  77. ;
  78. }
  79. modal.instantiate();
  80. },
  81. instantiate: function() {
  82. modal.verbose('Storing instance of modal');
  83. instance = modal;
  84. $modal
  85. .data(moduleNamespace, instance)
  86. ;
  87. },
  88. destroy: function() {
  89. modal.verbose('Destroying previous modal');
  90. $modal
  91. .off(eventNamespace)
  92. ;
  93. },
  94. event: {
  95. close: function() {
  96. modal.verbose('Close button pressed');
  97. $context.dimmer('hide');
  98. },
  99. keyboard: function(event) {
  100. var
  101. keyCode = event.which,
  102. escapeKey = 27
  103. ;
  104. if(keyCode == escapeKey) {
  105. modal.debug('Escape key pressed hiding modal');
  106. $context.dimmer('hide');
  107. event.preventDefault();
  108. }
  109. },
  110. resize: function() {
  111. modal.cache.sizes();
  112. if( $modal.is(':visible') ) {
  113. modal.set.type();
  114. modal.set.position();
  115. }
  116. }
  117. },
  118. toggle: function() {
  119. if( modal.is.active() ) {
  120. modal.hide();
  121. }
  122. else {
  123. modal.show();
  124. }
  125. },
  126. show: function() {
  127. modal.debug('Showing modal');
  128. modal.set.type();
  129. modal.set.position();
  130. modal.hideAll();
  131. if(settings.transition && $.fn.transition !== undefined) {
  132. $modal
  133. .transition(settings.transition + ' in', settings.duration, modal.set.active)
  134. ;
  135. }
  136. else {
  137. $modal
  138. .fadeIn(settings.duration, settings.easing, modal.set.active)
  139. ;
  140. }
  141. modal.debug('Triggering dimmer');
  142. $context.dimmer('show');
  143. },
  144. hide: function() {
  145. modal.debug('Hiding modal');
  146. // remove keyboard detection
  147. $document
  148. .off('keyup.' + namespace)
  149. ;
  150. if(settings.transition && $.fn.transition !== undefined) {
  151. $modal
  152. .transition(settings.transition + ' out', settings.duration, modal.remove.active)
  153. ;
  154. }
  155. else {
  156. $modal
  157. .fadeOut(settings.duration, settings.easing, modal.remove.active)
  158. ;
  159. }
  160. },
  161. hideAll: function() {
  162. $otherModals
  163. .filter(':visible')
  164. .modal('hide')
  165. ;
  166. },
  167. add: {
  168. keyboardShortcuts: function() {
  169. modal.verbose('Adding keyboard shortcuts');
  170. $document
  171. .on('keyup' + eventNamespace, modal.event.keyboard)
  172. ;
  173. }
  174. },
  175. remove: {
  176. active: function() {
  177. $modal.removeClass(className.active);
  178. },
  179. keyboardShortcuts: function() {
  180. modal.verbose('Removing keyboard shortcuts');
  181. $document
  182. .off('keyup' + eventNamespace)
  183. ;
  184. }
  185. },
  186. cache: {
  187. sizes: function() {
  188. modal.cache = {
  189. height : $modal.outerHeight() + settings.offset,
  190. contextHeight : (settings.context == 'body')
  191. ? $(window).height()
  192. : $context.height()
  193. };
  194. console.log($modal);
  195. modal.debug('Caching modal and container sizes', modal.cache);
  196. }
  197. },
  198. can: {
  199. fit: function() {
  200. return (modal.cache.height < modal.cache.contextHeight);
  201. }
  202. },
  203. is: {
  204. active: function() {
  205. return $modal.hasClass(className.active);
  206. }
  207. },
  208. set: {
  209. active: function() {
  210. $modal.addClass('active');
  211. },
  212. type: function() {
  213. if(modal.can.fit()) {
  214. modal.verbose('Modal fits on screen');
  215. $modal.removeClass(className.scrolling);
  216. }
  217. else {
  218. modal.verbose('Modal cannot fit on screen setting to scrolling');
  219. $modal.addClass(className.scrolling);
  220. }
  221. },
  222. position: function() {
  223. modal.verbose('Centering modal on page', modal.cache.height / 2);
  224. if(modal.can.fit()) {
  225. $modal
  226. .css({
  227. marginTop: -(modal.cache.height / 2)
  228. })
  229. ;
  230. }
  231. else {
  232. $modal
  233. .css({
  234. top: $context.prop('scrollTop')
  235. })
  236. ;
  237. }
  238. }
  239. },
  240. setting: function(name, value) {
  241. if(value !== undefined) {
  242. if( $.isPlainObject(name) ) {
  243. $.extend(true, settings, name);
  244. }
  245. else {
  246. settings[name] = value;
  247. }
  248. }
  249. else {
  250. return settings[name];
  251. }
  252. },
  253. internal: function(name, value) {
  254. if(value !== undefined) {
  255. if( $.isPlainObject(name) ) {
  256. $.extend(true, modal, name);
  257. }
  258. else {
  259. modal[name] = value;
  260. }
  261. }
  262. else {
  263. return modal[name];
  264. }
  265. },
  266. debug: function() {
  267. if(settings.debug) {
  268. if(settings.performance) {
  269. modal.performance.log(arguments);
  270. }
  271. else {
  272. modal.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  273. modal.debug.apply(console, arguments);
  274. }
  275. }
  276. },
  277. verbose: function() {
  278. if(settings.verbose && settings.debug) {
  279. if(settings.performance) {
  280. modal.performance.log(arguments);
  281. }
  282. else {
  283. modal.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  284. modal.verbose.apply(console, arguments);
  285. }
  286. }
  287. },
  288. error: function() {
  289. modal.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  290. modal.error.apply(console, arguments);
  291. },
  292. performance: {
  293. log: function(message) {
  294. var
  295. currentTime,
  296. executionTime,
  297. previousTime
  298. ;
  299. if(settings.performance) {
  300. currentTime = new Date().getTime();
  301. previousTime = time || currentTime;
  302. executionTime = currentTime - previousTime;
  303. time = currentTime;
  304. performance.push({
  305. 'Element' : element,
  306. 'Name' : message[0],
  307. 'Arguments' : [].slice.call(message, 1) || '',
  308. 'Execution Time' : executionTime
  309. });
  310. }
  311. clearTimeout(modal.performance.timer);
  312. modal.performance.timer = setTimeout(modal.performance.display, 100);
  313. },
  314. display: function() {
  315. var
  316. title = settings.name + ':',
  317. totalTime = 0
  318. ;
  319. time = false;
  320. clearTimeout(modal.performance.timer);
  321. $.each(performance, function(index, data) {
  322. totalTime += data['Execution Time'];
  323. });
  324. title += ' ' + totalTime + 'ms';
  325. if(moduleSelector) {
  326. title += ' \'' + moduleSelector + '\'';
  327. }
  328. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  329. console.groupCollapsed(title);
  330. if(console.table) {
  331. console.table(performance);
  332. }
  333. else {
  334. $.each(performance, function(index, data) {
  335. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  336. });
  337. }
  338. console.groupEnd();
  339. }
  340. performance = [];
  341. }
  342. },
  343. invoke: function(query, passedArguments, context) {
  344. var
  345. maxDepth,
  346. found,
  347. response
  348. ;
  349. passedArguments = passedArguments || queryArguments;
  350. context = element || context;
  351. if(typeof query == 'string' && instance !== undefined) {
  352. query = query.split(/[\. ]/);
  353. maxDepth = query.length - 1;
  354. $.each(query, function(depth, value) {
  355. var camelCaseValue = (depth != maxDepth)
  356. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  357. : query
  358. ;
  359. if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  360. instance = instance[value];
  361. }
  362. else if( $.isPlainObject( instance[camelCaseValue] ) && (depth != maxDepth) ) {
  363. instance = instance[camelCaseValue];
  364. }
  365. else if( instance[value] !== undefined ) {
  366. found = instance[value];
  367. return false;
  368. }
  369. else if( instance[camelCaseValue] !== undefined ) {
  370. found = instance[camelCaseValue];
  371. return false;
  372. }
  373. else {
  374. modal.error(error.method);
  375. return false;
  376. }
  377. });
  378. }
  379. if ( $.isFunction( found ) ) {
  380. response = found.apply(context, passedArguments);
  381. }
  382. else if(found !== undefined) {
  383. response = found;
  384. }
  385. if($.isArray(invokedResponse)) {
  386. invokedResponse.push(response);
  387. }
  388. else if(typeof invokedResponse == 'string') {
  389. invokedResponse = [invokedResponse, response];
  390. }
  391. else if(response !== undefined) {
  392. invokedResponse = response;
  393. }
  394. return found;
  395. }
  396. };
  397. if(methodInvoked) {
  398. if(instance === undefined) {
  399. modal.initialize();
  400. }
  401. invokedResponse = modal.invoke(query);
  402. }
  403. else {
  404. if(instance !== undefined) {
  405. modal.destroy();
  406. }
  407. modal.initialize();
  408. }
  409. })
  410. ;
  411. return (invokedResponse !== undefined)
  412. ? invokedResponse
  413. : this
  414. ;
  415. };
  416. $.fn.modal.settings = {
  417. name : 'Modal',
  418. namespace : 'modal',
  419. verbose : true,
  420. debug : true,
  421. performance : true,
  422. offset : 0,
  423. transition : 'scale',
  424. duration : 500,
  425. easing : 'easeOutExpo',
  426. closable : true,
  427. context : 'body',
  428. onShow : function(){},
  429. onHide : function(){},
  430. selector : {
  431. closeButton : '.close, .actions .button',
  432. dimmer: '.ui.dimmer'
  433. },
  434. error : {
  435. method : 'The method you called is not defined.'
  436. },
  437. className : {
  438. scrolling : 'scrolling'
  439. },
  440. };
  441. })( jQuery, window , document );