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.

605 lines
17 KiB

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