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.

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