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.

597 lines
17 KiB

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