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.

428 lines
11 KiB

  1. /*
  2. arcticModal jQuery plugin
  3. Version: 0.3
  4. Author: Sergey Predvoditelev (sergey.predvoditelev@gmail.com)
  5. Company: Arctic Laboratory (http://arcticlab.ru/)
  6. Docs & Examples: http://arcticlab.ru/arcticmodal/
  7. */
  8. (function($) {
  9. var default_options = {
  10. type: 'html', // ajax или html
  11. content: '',
  12. url: '',
  13. ajax: {},
  14. ajax_request: null,
  15. closeOnEsc: true,
  16. closeOnOverlayClick: true,
  17. clone: false,
  18. overlay: {
  19. block: undefined,
  20. tpl: '<div class="arcticmodal-overlay"></div>',
  21. css: {
  22. backgroundColor: '#000',
  23. opacity: .6
  24. }
  25. },
  26. container: {
  27. block: undefined,
  28. tpl: '<div class="arcticmodal-container"><table class="arcticmodal-container_i"><tr><td class="arcticmodal-container_i2"></td></tr></table></div>'
  29. },
  30. wrap: undefined,
  31. body: undefined,
  32. errors: {
  33. tpl: '<div class="arcticmodal-error arcticmodal-close"></div>',
  34. autoclose_delay: 2000,
  35. ajax_unsuccessful_load: 'Error'
  36. },
  37. openEffect: {
  38. type: 'fade',
  39. speed: 400
  40. },
  41. closeEffect: {
  42. type: 'fade',
  43. speed: 400
  44. },
  45. beforeOpen: $.noop,
  46. afterOpen: $.noop,
  47. beforeClose: $.noop,
  48. afterClose: $.noop,
  49. afterLoading: $.noop,
  50. afterLoadingOnShow: $.noop,
  51. errorLoading: $.noop
  52. };
  53. var modalID = 0;
  54. var modals = $([]);
  55. var utils = {
  56. // Определяет произошло ли событие e вне блока block
  57. isEventOut: function(blocks, e) {
  58. var r = true;
  59. $(blocks).each(function() {
  60. if ($(e.target).get(0)==$(this).get(0)) r = false;
  61. if ($(e.target).closest('HTML', $(this).get(0)).length==0) r = false;
  62. });
  63. return r;
  64. }
  65. };
  66. var modal = {
  67. // Возвращает элемент, которым был вызван плагин
  68. getParentEl: function(el) {
  69. var r = $(el);
  70. if (r.data('arcticmodal')) return r;
  71. r = $(el).closest('.arcticmodal-container').data('arcticmodalParentEl');
  72. if (r) return r;
  73. return false;
  74. },
  75. // Переход
  76. transition: function(el, action, options, callback) {
  77. callback = callback==undefined ? $.noop : callback;
  78. switch (options.type) {
  79. case 'fade':
  80. action=='show' ? el.fadeIn(options.speed, callback) : el.fadeOut(options.speed, callback);
  81. break;
  82. case 'none':
  83. action=='show' ? el.show() : el.hide();
  84. callback();
  85. break;
  86. }
  87. },
  88. // Подготвка содержимого окна
  89. prepare_body: function(D, $this) {
  90. // Обработчик закрытия
  91. $('.arcticmodal-close', D.body).unbind('click.arcticmodal').bind('click.arcticmodal', function() {
  92. $this.arcticmodal('close');
  93. return false;
  94. });
  95. },
  96. // Инициализация элемента
  97. init_el: function($this, options) {
  98. var D = $this.data('arcticmodal');
  99. if (D) return;
  100. D = options;
  101. modalID++;
  102. D.modalID = modalID;
  103. // Overlay
  104. D.overlay.block = $(D.overlay.tpl);
  105. D.overlay.block.css(D.overlay.css);
  106. // Container
  107. D.container.block = $(D.container.tpl);
  108. // BODY
  109. D.body = $('.arcticmodal-container_i2', D.container.block);
  110. if (options.clone) {
  111. D.body.html($this.clone(true));
  112. } else {
  113. $this.before('<div id="arcticmodalReserve' + D.modalID + '" style="display: none" />');
  114. D.body.html($this);
  115. }
  116. // Подготовка содержимого
  117. modal.prepare_body(D, $this);
  118. // Закрытие при клике на overlay
  119. if (D.closeOnOverlayClick)
  120. D.overlay.block.add(D.container.block).click(function(e) {
  121. if (utils.isEventOut($('>*', D.body), e))
  122. $this.arcticmodal('close');
  123. });
  124. // Запомним настройки
  125. D.container.block.data('arcticmodalParentEl', $this);
  126. $this.data('arcticmodal', D);
  127. modals = $.merge(modals, $this);
  128. // Показать
  129. $.proxy(actions.show, $this)();
  130. if (D.type=='html') return $this;
  131. // Ajax-загрузка
  132. if (D.ajax.beforeSend!=undefined) {
  133. var fn_beforeSend = D.ajax.beforeSend;
  134. delete D.ajax.beforeSend;
  135. }
  136. if (D.ajax.success!=undefined) {
  137. var fn_success = D.ajax.success;
  138. delete D.ajax.success;
  139. }
  140. if (D.ajax.error!=undefined) {
  141. var fn_error = D.ajax.error;
  142. delete D.ajax.error;
  143. }
  144. var o = $.extend(true, {
  145. url: D.url,
  146. beforeSend: function() {
  147. if (fn_beforeSend==undefined) {
  148. D.body.html('<div class="arcticmodal-loading" />');
  149. } else {
  150. fn_beforeSend(D, $this);
  151. }
  152. },
  153. success: function(responce) {
  154. // Событие после загрузки до показа содержимого
  155. $this.trigger('afterLoading');
  156. D.afterLoading(D, $this, responce);
  157. if (fn_success==undefined) {
  158. D.body.html(responce);
  159. } else {
  160. fn_success(D, $this, responce);
  161. }
  162. modal.prepare_body(D, $this);
  163. // Событие после загрузки после отображения содержимого
  164. $this.trigger('afterLoadingOnShow');
  165. D.afterLoadingOnShow(D, $this, responce);
  166. },
  167. error: function() {
  168. // Событие при ошибке загрузки
  169. $this.trigger('errorLoading');
  170. D.errorLoading(D, $this);
  171. if (fn_error==undefined) {
  172. D.body.html(D.errors.tpl);
  173. $('.arcticmodal-error', D.body).html(D.errors.ajax_unsuccessful_load);
  174. $('.arcticmodal-close', D.body).click(function() {
  175. $this.arcticmodal('close');
  176. return false;
  177. });
  178. if (D.errors.autoclose_delay)
  179. setTimeout(function() {
  180. $this.arcticmodal('close');
  181. }, D.errors.autoclose_delay);
  182. } else {
  183. fn_error(D, $this);
  184. }
  185. }
  186. }, D.ajax);
  187. D.ajax_request = $.ajax(o);
  188. // Запомнить настройки
  189. $this.data('arcticmodal', D);
  190. },
  191. // Инициализация
  192. init: function(options) {
  193. options = $.extend(true, {}, default_options, options);
  194. if ($.isFunction(this)) {
  195. if (options==undefined) {
  196. $.error('jquery.arcticmodal: Uncorrect parameters');
  197. return;
  198. }
  199. if (options.type=='') {
  200. $.error('jquery.arcticmodal: Don\'t set parameter "type"');
  201. return;
  202. }
  203. switch (options.type) {
  204. case 'html':
  205. if (options.content=='') {
  206. $.error('jquery.arcticmodal: Don\'t set parameter "content"');
  207. return
  208. }
  209. var c = options.content;
  210. options.content = '';
  211. return modal.init_el($(c), options);
  212. break;
  213. case 'ajax':
  214. if (options.url=='') {
  215. $.error('jquery.arcticmodal: Don\'t set parameter "url"');
  216. return;
  217. }
  218. return modal.init_el($('<div />'), options);
  219. break;
  220. }
  221. } else {
  222. return this.each(function() {
  223. modal.init_el($(this), $.extend(true, {}, options));
  224. });
  225. }
  226. }
  227. };
  228. var actions = {
  229. // Показать
  230. show: function() {
  231. var $this = modal.getParentEl(this);
  232. if ($this===false) {
  233. $.error('jquery.arcticmodal: Uncorrect call');
  234. return;
  235. }
  236. var D = $this.data('arcticmodal');
  237. // Добавить overlay и container
  238. D.overlay.block.hide();
  239. D.container.block.hide();
  240. $('BODY').append(D.overlay.block);
  241. $('BODY').append(D.container.block);
  242. // Событие
  243. D.beforeOpen(D, $this);
  244. $this.trigger('beforeOpen');
  245. // Wrap
  246. if (D.wrap.css('overflow')!='hidden') {
  247. D.wrap.data('arcticmodalOverflow', D.wrap.css('overflow'));
  248. var w1 = D.wrap.outerWidth(true);
  249. D.wrap.css('overflow', 'hidden');
  250. var w2 = D.wrap.outerWidth(true);
  251. if (w2!=w1)
  252. D.wrap.css('marginRight', (w2 - w1) + 'px');
  253. }
  254. // Скрыть предыдущие оверлеи
  255. modals.not($this).each(function() {
  256. var d = $(this).data('arcticmodal');
  257. d.overlay.block.hide();
  258. });
  259. // Показать
  260. modal.transition(D.overlay.block, 'show', modals.length>1 ? {type: 'none'} : D.openEffect);
  261. modal.transition(D.container.block, 'show', modals.length>1 ? {type: 'none'} : D.openEffect, function() {
  262. D.afterOpen(D, $this);
  263. $this.trigger('afterOpen');
  264. });
  265. return $this;
  266. },
  267. // Закрыть
  268. close: function() {
  269. if ($.isFunction(this)) {
  270. modals.each(function() {
  271. $(this).arcticmodal('close');
  272. });
  273. } else {
  274. return this.each(function() {
  275. var $this = modal.getParentEl(this);
  276. if ($this===false) {
  277. $.error('jquery.arcticmodal: Uncorrect call');
  278. return;
  279. }
  280. var D = $this.data('arcticmodal');
  281. // Событие перед закрытием
  282. if (D.beforeClose(D, $this)===false) return;
  283. $this.trigger('beforeClose');
  284. // Показать предыдущие оверлеи
  285. modals.not($this).last().each(function() {
  286. var d = $(this).data('arcticmodal');
  287. d.overlay.block.show();
  288. });
  289. modal.transition(D.overlay.block, 'hide', modals.length>1 ? {type: 'none'} : D.closeEffect);
  290. modal.transition(D.container.block, 'hide', modals.length>1 ? {type: 'none'} : D.closeEffect, function() {
  291. // Событие после закрытия
  292. D.afterClose(D, $this);
  293. $this.trigger('afterClose');
  294. // Если не клонировали - вернём на место
  295. if (!D.clone)
  296. $('#arcticmodalReserve' + D.modalID).replaceWith(D.body.find('>*'));
  297. D.overlay.block.remove();
  298. D.container.block.remove();
  299. $this.data('arcticmodal', null);
  300. if (!$('.arcticmodal-container').length) {
  301. if (D.wrap.data('arcticmodalOverflow'))
  302. D.wrap.css('overflow', D.wrap.data('arcticmodalOverflow'));
  303. D.wrap.css('marginRight', 0);
  304. }
  305. });
  306. if (D.type=='ajax')
  307. D.ajax_request.abort();
  308. modals = modals.not($this);
  309. });
  310. }
  311. },
  312. // Установить опции по-умолчанию
  313. setDefault: function(options) {
  314. $.extend(true, default_options, options);
  315. }
  316. };
  317. $(function() {
  318. default_options.wrap = $((document.all && !document.querySelector) ? 'html' : 'body');
  319. });
  320. // Закрытие при нажатии Escape
  321. $(document).bind('keyup.arcticmodal', function(e) {
  322. var m = modals.last();
  323. if (!m.length) return;
  324. var D = m.data('arcticmodal');
  325. if (D.closeOnEsc && (e.keyCode===27))
  326. m.arcticmodal('close');
  327. });
  328. $.arcticmodal = $.fn.arcticmodal = function(method) {
  329. if (actions[method]) {
  330. return actions[method].apply(this, Array.prototype.slice.call(arguments, 1));
  331. } else if (typeof method==='object' || !method) {
  332. return modal.init.apply(this, arguments);
  333. } else {
  334. $.error('jquery.arcticmodal: Method ' + method + ' does not exist');
  335. }
  336. };
  337. })(jQuery);