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.

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