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.

361 lines
10 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. $.dimScreen = function(parameters) {
  10. var
  11. // if parameter is string it is callback function
  12. settings = (typeof parameters == 'function')
  13. ? $.extend({}, $.fn.modal.settings, { dim: parameters })
  14. : $.extend({}, $.fn.modal.settings, parameters),
  15. $context = $(settings.context),
  16. $dimmer = $context.children(settings.selector.dimmer),
  17. dimmerExists = ($dimmer.size() > 0),
  18. currentOpacity = $dimmer.css('opacity')
  19. ;
  20. if(!dimmerExists) {
  21. $dimmer = $('<div/>')
  22. .attr('id','dimmer')
  23. .html('<div class="content"></div>')
  24. ;
  25. $context
  26. .append($dimmer)
  27. ;
  28. }
  29. if(currentOpacity != settings.opacity) {
  30. $dimmer
  31. .one('click', function() {
  32. settings.unDim();
  33. $.unDimScreen();
  34. })
  35. ;
  36. if(settings.duration === 0) {
  37. $dimmer
  38. .css({
  39. visibility : 'visible'
  40. })
  41. .find('.content')
  42. .css({
  43. opacity : settings.opacity,
  44. visibility : 'visible'
  45. })
  46. ;
  47. settings.dim();
  48. }
  49. else {
  50. $dimmer
  51. .css({
  52. visibility : 'visible'
  53. })
  54. .find('.content')
  55. .css({
  56. opacity : 0,
  57. visibility : 'visible'
  58. })
  59. .fadeTo(settings.duration, settings.opacity, settings.dim)
  60. ;
  61. }
  62. }
  63. return this;
  64. };
  65. $.unDimScreen = function(parameters) {
  66. var
  67. settings = (typeof parameters == 'function')
  68. ? $.extend({}, $.fn.modal.settings, { unDim: parameters })
  69. : $.extend({}, $.fn.modal.settings, parameters),
  70. $context = $(settings.context),
  71. $dimmer = $context.children(settings.selector.dimmer),
  72. dimmerExists = ($dimmer.size() > 0)
  73. ;
  74. if(dimmerExists) {
  75. // callback before unDim
  76. settings.unDim();
  77. if(settings.duration === 0) {
  78. $dimmer
  79. .css({
  80. visibility: 'hidden'
  81. })
  82. .remove()
  83. ;
  84. }
  85. else {
  86. $dimmer
  87. .find('.content')
  88. .fadeTo(settings.duration, 0, function(){
  89. $dimmer.remove();
  90. })
  91. ;
  92. }
  93. }
  94. return this;
  95. };
  96. $.fn.modal = function(parameters) {
  97. var
  98. settings = $.extend(true, {}, $.fn.modal.settings, parameters),
  99. // make arguments available
  100. query = arguments[0],
  101. passedArguments = [].slice.call(arguments, 1),
  102. invokedResponse
  103. ;
  104. $(this)
  105. .each(function() {
  106. var
  107. $modal = $(this),
  108. $closeButton = $modal.find(settings.selector.closeButton),
  109. $dimmer = $(settings.context).find(settings.selector.dimmer),
  110. $modals = $(settings.context).children(settings.selector.modal),
  111. $otherModals = $modals.not($modal),
  112. instance = $modal.data('module-' + settings.namespace),
  113. methodInvoked = (typeof query == 'string'),
  114. className = settings.className,
  115. namespace = settings.namespace,
  116. module
  117. ;
  118. module = {
  119. initialize: function() {
  120. // attach events
  121. $modal
  122. .on('modalShow.' + namespace, module.show)
  123. .on('modalHide.' + namespace, module.hide)
  124. .data('module-' + namespace, module)
  125. ;
  126. },
  127. show: function() {
  128. var
  129. modalHeight = $modal.outerHeight(),
  130. windowHeight = $(window).height(),
  131. cantFit = (modalHeight > windowHeight),
  132. modalType = (cantFit)
  133. ? 'absolute'
  134. : 'fixed',
  135. topCentering,
  136. offsetTop,
  137. finalPosition,
  138. startPosition
  139. ;
  140. $modal
  141. .addClass(modalType)
  142. ;
  143. topCentering = (cantFit)
  144. ? '0'
  145. : '50%'
  146. ;
  147. offsetTop = (cantFit)
  148. ? (windowHeight / 8)
  149. : -( (modalHeight - settings.closeSpacing) / 2)
  150. ;
  151. finalPosition = ($modal.css('position') == 'absolute')
  152. ? offsetTop + $(window).prop('pageYOffset')
  153. : offsetTop
  154. ;
  155. startPosition = finalPosition + settings.animationOffset;
  156. // set top margin as offset
  157. if($.fn.popIn !== undefined) {
  158. $modal
  159. .css({
  160. display : 'block',
  161. opacity : 0,
  162. top: topCentering,
  163. marginTop : finalPosition + 'px'
  164. })
  165. .popIn()
  166. ;
  167. }
  168. else {
  169. $modal
  170. .css({
  171. display : 'block',
  172. opacity : 0,
  173. top: topCentering,
  174. marginTop : startPosition + 'px'
  175. })
  176. .animate({
  177. opacity : 1,
  178. marginTop : finalPosition + 'px'
  179. }, (settings.duration + 300), settings.easing)
  180. ;
  181. }
  182. if( $otherModals.is(':visible') ) {
  183. $otherModals
  184. .filter(':visible')
  185. .hide()
  186. ;
  187. }
  188. $.dimScreen({
  189. context : settings.context,
  190. duration : 0,
  191. dim : function() {
  192. $(document)
  193. .on('keyup.' + namespace, function(event) {
  194. var
  195. keyCode = event.which,
  196. escapeKey = 27
  197. ;
  198. switch(keyCode) {
  199. case escapeKey:
  200. $modal.modal('hide');
  201. event.preventDefault();
  202. break;
  203. }
  204. })
  205. ;
  206. $closeButton
  207. .one('click', function() {
  208. $modal.modal('hide');
  209. })
  210. ;
  211. settings.dim();
  212. },
  213. unDim: function() {
  214. $modal.modal('hide');
  215. $closeButton.unbind('click');
  216. }
  217. });
  218. },
  219. hide: function() {
  220. // remove keyboard detection
  221. $(document)
  222. .off('keyup.' + namespace)
  223. ;
  224. $.unDimScreen({
  225. duration: 0,
  226. unDim: function() {
  227. $modal
  228. .popOut(200)
  229. ;
  230. settings.unDim();
  231. }
  232. });
  233. },
  234. /* standard module */
  235. setting: function(name, value) {
  236. if(value === undefined) {
  237. return settings[name];
  238. }
  239. settings[name] = value;
  240. },
  241. debug: function() {
  242. var
  243. output = [],
  244. message = settings.moduleName + ': ' + arguments[0],
  245. variables = [].slice.call( arguments, 1 ),
  246. log = console.info || console.log || function(){}
  247. ;
  248. if(settings.debug) {
  249. output.push(message);
  250. log.apply(console, output.concat(variables) );
  251. }
  252. },
  253. error: function() {
  254. var
  255. output = [],
  256. errorMessage = settings.moduleName + ': ' + arguments[0],
  257. variables = [].slice.call( arguments, 1 ),
  258. log = console.warn || console.log || function(){}
  259. ;
  260. if(settings.debug) {
  261. output.push(errorMessage);
  262. output.concat(variables);
  263. log.apply(console, output.concat(variables) );
  264. }
  265. },
  266. invoke: function(query, context, passedArguments) {
  267. var
  268. maxDepth,
  269. found
  270. ;
  271. passedArguments = passedArguments || [].slice.call( arguments, 2 );
  272. if(typeof query == 'string' && instance !== undefined) {
  273. query = query.split('.');
  274. maxDepth = query.length - 1;
  275. $.each(query, function(depth, value) {
  276. if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  277. instance = instance[value];
  278. return true;
  279. }
  280. else if( instance[value] !== undefined ) {
  281. found = instance[value];
  282. return true;
  283. }
  284. module.error(settings.errors.method);
  285. return false;
  286. });
  287. }
  288. if ( $.isFunction( found ) ) {
  289. return found.apply(context, passedArguments);
  290. }
  291. // return retrieved variable or chain
  292. return found;
  293. }
  294. };
  295. // check for invoking internal method
  296. if(methodInvoked) {
  297. invokedResponse = module.invoke(query, this, passedArguments);
  298. }
  299. // otherwise initialize
  300. else {
  301. module.initialize();
  302. }
  303. })
  304. ;
  305. // chain or return queried method
  306. return (invokedResponse !== undefined)
  307. ? invokedResponse
  308. : this
  309. ;
  310. };
  311. $.fn.modal.settings = {
  312. moduleName : 'Modal',
  313. debug : false,
  314. namespace : 'modal',
  315. errors: {
  316. method : 'The method you called is not defined'
  317. },
  318. dim : function(){},
  319. unDim : function(){},
  320. hide : function(){},
  321. show : function(){},
  322. context : 'body',
  323. opacity : 0.8,
  324. closeSpacing : 25,
  325. animationOffset : 15,
  326. duration : 400,
  327. easing : 'easeOutExpo',
  328. selector : {
  329. dimmer : '#dimmer',
  330. modal : '.modal',
  331. closeButton : '.close'
  332. }
  333. };
  334. })( jQuery, window , document );