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.

363 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. .removeClass('absolute fixed')
  142. .addClass(modalType)
  143. ;
  144. topCentering = (cantFit)
  145. ? '0'
  146. : '50%'
  147. ;
  148. offsetTop = (cantFit)
  149. ? (windowHeight / 8)
  150. : -( (modalHeight - settings.closeSpacing) / 2)
  151. ;
  152. finalPosition = ($modal.css('position') == 'absolute')
  153. ? offsetTop + $(window).prop('pageYOffset')
  154. : offsetTop
  155. ;
  156. startPosition = finalPosition + settings.animationOffset;
  157. // set top margin as offset
  158. if($.fn.popIn !== undefined) {
  159. $modal
  160. .css({
  161. display : 'block',
  162. opacity : 0,
  163. top: topCentering,
  164. marginTop : finalPosition + 'px'
  165. })
  166. .popIn()
  167. ;
  168. }
  169. else {
  170. $modal
  171. .css({
  172. display : 'block',
  173. opacity : 0,
  174. top: topCentering,
  175. marginTop : startPosition + 'px'
  176. })
  177. .animate({
  178. opacity : 1,
  179. marginTop : finalPosition + 'px'
  180. }, (settings.duration + 300), settings.easing)
  181. ;
  182. }
  183. if( $otherModals.is(':visible') ) {
  184. $otherModals
  185. .filter(':visible')
  186. .hide()
  187. ;
  188. }
  189. $.dimScreen({
  190. context : settings.context,
  191. duration : 0,
  192. dim : function() {
  193. $(document)
  194. .on('keyup.' + namespace, function(event) {
  195. var
  196. keyCode = event.which,
  197. escapeKey = 27
  198. ;
  199. switch(keyCode) {
  200. case escapeKey:
  201. $modal.modal('hide');
  202. event.preventDefault();
  203. break;
  204. }
  205. })
  206. ;
  207. $closeButton
  208. .one('click', function() {
  209. $modal.modal('hide');
  210. })
  211. ;
  212. settings.dim();
  213. },
  214. unDim: function() {
  215. $modal.modal('hide');
  216. $closeButton.unbind('click');
  217. }
  218. });
  219. },
  220. hide: function() {
  221. // remove keyboard detection
  222. $(document)
  223. .off('keyup.' + namespace)
  224. ;
  225. $.unDimScreen({
  226. duration: 0,
  227. unDim: function() {
  228. $modal
  229. .popOut(200)
  230. ;
  231. settings.unDim();
  232. }
  233. });
  234. },
  235. /* standard module */
  236. setting: function(name, value) {
  237. if(value === undefined) {
  238. return settings[name];
  239. }
  240. settings[name] = value;
  241. },
  242. debug: function() {
  243. var
  244. output = [],
  245. message = settings.moduleName + ': ' + arguments[0],
  246. variables = [].slice.call( arguments, 1 ),
  247. log = console.info || console.log || function(){}
  248. ;
  249. if(settings.debug) {
  250. output.push(message);
  251. log.apply(console, output.concat(variables) );
  252. }
  253. },
  254. error: function() {
  255. var
  256. output = [],
  257. errorMessage = settings.moduleName + ': ' + arguments[0],
  258. variables = [].slice.call( arguments, 1 ),
  259. log = console.warn || console.log || function(){}
  260. ;
  261. if(settings.debug) {
  262. output.push(errorMessage);
  263. output.concat(variables);
  264. log.apply(console, output.concat(variables) );
  265. }
  266. },
  267. invoke: function(query, context, passedArguments) {
  268. var
  269. maxDepth,
  270. found
  271. ;
  272. passedArguments = passedArguments || [].slice.call( arguments, 2 );
  273. if(typeof query == 'string' && instance !== undefined) {
  274. query = query.split(/[\. ]/);
  275. maxDepth = query.length - 1;
  276. $.each(query, function(depth, value) {
  277. if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  278. instance = instance[value];
  279. return true;
  280. }
  281. else if( instance[value] !== undefined ) {
  282. found = instance[value];
  283. return true;
  284. }
  285. module.error(settings.errors.method);
  286. return false;
  287. });
  288. }
  289. if ( $.isFunction( found ) ) {
  290. return found.apply(context, passedArguments);
  291. }
  292. // return retrieved variable or chain
  293. return found;
  294. }
  295. };
  296. // check for invoking internal method
  297. if(methodInvoked) {
  298. invokedResponse = module.invoke(query, this, passedArguments);
  299. }
  300. // otherwise initialize
  301. else {
  302. module.initialize();
  303. }
  304. })
  305. ;
  306. // chain or return queried method
  307. return (invokedResponse)
  308. ? invokedResponse
  309. : this
  310. ;
  311. };
  312. $.fn.modal.settings = {
  313. moduleName : 'Modal',
  314. debug : false,
  315. namespace : 'modal',
  316. errors: {
  317. method : 'The method you called is not defined'
  318. },
  319. dim : function(){},
  320. unDim : function(){},
  321. hide : function(){},
  322. show : function(){},
  323. context : 'body',
  324. opacity : 0.8,
  325. closeSpacing : 25,
  326. animationOffset : 15,
  327. duration : 400,
  328. easing : 'easeOutExpo',
  329. selector : {
  330. dimmer : '#dimmer',
  331. modal : '.modal',
  332. closeButton : '.close'
  333. }
  334. };
  335. })( jQuery, window , document );