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.

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