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.

440 lines
12 KiB

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