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.

477 lines
13 KiB

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