jquery.superfish.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. * jQuery Superfish Menu Plugin - v1.7.9
  3. * Copyright (c) 2016 Joel Birch
  4. *
  5. * Dual licensed under the MIT and GPL licenses:
  6. * http://www.opensource.org/licenses/mit-license.php
  7. * http://www.gnu.org/licenses/gpl.html
  8. */
  9. ;(function ($, w) {
  10. "use strict";
  11. var methods = (function () {
  12. // private properties and methods go here
  13. var c = {
  14. bcClass: 'sf-breadcrumb',
  15. menuClass: 'sf-js-enabled',
  16. anchorClass: 'sf-with-ul',
  17. menuArrowClass: 'sf-arrows'
  18. },
  19. ios = (function () {
  20. var ios = /^(?![\w\W]*Windows Phone)[\w\W]*(iPhone|iPad|iPod)/i.test(navigator.userAgent);
  21. if (ios) {
  22. // tap anywhere on iOS to unfocus a submenu
  23. $('html').css('cursor', 'pointer').on('click', $.noop);
  24. }
  25. return ios;
  26. })(),
  27. wp7 = (function () {
  28. var style = document.documentElement.style;
  29. return ('behavior' in style && 'fill' in style && /iemobile/i.test(navigator.userAgent));
  30. })(),
  31. unprefixedPointerEvents = (function () {
  32. return (!!w.PointerEvent);
  33. })(),
  34. toggleMenuClasses = function ($menu, o, add) {
  35. var classes = c.menuClass,
  36. method;
  37. if (o.cssArrows) {
  38. classes += ' ' + c.menuArrowClass;
  39. }
  40. method = (add) ? 'addClass' : 'removeClass';
  41. $menu[method](classes);
  42. },
  43. setPathToCurrent = function ($menu, o) {
  44. return $menu.find('li.' + o.pathClass).slice(0, o.pathLevels)
  45. .addClass(o.hoverClass + ' ' + c.bcClass)
  46. .filter(function () {
  47. return ($(this).children(o.popUpSelector).hide().show().length);
  48. }).removeClass(o.pathClass);
  49. },
  50. toggleAnchorClass = function ($li, add) {
  51. var method = (add) ? 'addClass' : 'removeClass';
  52. $li.children('a')[method](c.anchorClass);
  53. },
  54. toggleTouchAction = function ($menu) {
  55. var msTouchAction = $menu.css('ms-touch-action');
  56. var touchAction = $menu.css('touch-action');
  57. touchAction = touchAction || msTouchAction;
  58. touchAction = (touchAction === 'pan-y') ? 'auto' : 'pan-y';
  59. $menu.css({
  60. 'ms-touch-action': touchAction,
  61. 'touch-action': touchAction
  62. });
  63. },
  64. getMenu = function ($el) {
  65. return $el.closest('.' + c.menuClass);
  66. },
  67. getOptions = function ($el) {
  68. return getMenu($el).data('sfOptions');
  69. },
  70. over = function () {
  71. var $this = $(this),
  72. o = getOptions($this);
  73. clearTimeout(o.sfTimer);
  74. $this.siblings().superfish('hide').end().superfish('show');
  75. },
  76. close = function (o) {
  77. o.retainPath = ($.inArray(this[0], o.$path) > -1);
  78. this.superfish('hide');
  79. if (!this.parents('.' + o.hoverClass).length) {
  80. o.onIdle.call(getMenu(this));
  81. if (o.$path.length) {
  82. $.proxy(over, o.$path)();
  83. }
  84. }
  85. },
  86. out = function () {
  87. var $this = $(this),
  88. o = getOptions($this);
  89. if (ios) {
  90. $.proxy(close, $this, o)();
  91. }
  92. else {
  93. clearTimeout(o.sfTimer);
  94. o.sfTimer = setTimeout($.proxy(close, $this, o), o.delay);
  95. }
  96. },
  97. touchHandler = function (e) {
  98. var $this = $(this),
  99. o = getOptions($this),
  100. $ul = $this.siblings(e.data.popUpSelector);
  101. if (o.onHandleTouch.call($ul) === false) {
  102. return this;
  103. }
  104. if ($ul.length > 0 && $ul.is(':hidden')) {
  105. $this.one('click.superfish', false);
  106. if (e.type === 'MSPointerDown' || e.type === 'pointerdown') {
  107. $this.trigger('focus');
  108. } else {
  109. $.proxy(over, $this.parent('li'))();
  110. }
  111. }
  112. },
  113. applyHandlers = function ($menu, o) {
  114. var targets = 'li:has(' + o.popUpSelector + ')';
  115. if ($.fn.hoverIntent && !o.disableHI) {
  116. $menu.hoverIntent(over, out, targets);
  117. }
  118. else {
  119. $menu
  120. .on('mouseenter.superfish', targets, over)
  121. .on('mouseleave.superfish', targets, out);
  122. }
  123. var touchevent = 'MSPointerDown.superfish';
  124. if (unprefixedPointerEvents) {
  125. touchevent = 'pointerdown.superfish';
  126. }
  127. if (!ios) {
  128. touchevent += ' touchend.superfish';
  129. }
  130. if (wp7) {
  131. touchevent += ' mousedown.superfish';
  132. }
  133. $menu
  134. .on('focusin.superfish', 'li', over)
  135. .on('focusout.superfish', 'li', out)
  136. .on(touchevent, 'a', o, touchHandler);
  137. };
  138. return {
  139. // public methods
  140. hide: function (instant) {
  141. if (this.length) {
  142. var $this = this,
  143. o = getOptions($this);
  144. if (!o) {
  145. return this;
  146. }
  147. var not = (o.retainPath === true) ? o.$path : '',
  148. $ul = $this.find('li.' + o.hoverClass).add(this).not(not).removeClass(o.hoverClass).children(o.popUpSelector),
  149. speed = o.speedOut;
  150. if (instant) {
  151. $ul.show();
  152. speed = 0;
  153. }
  154. o.retainPath = false;
  155. if (o.onBeforeHide.call($ul) === false) {
  156. return this;
  157. }
  158. $ul.stop(true, true).animate(o.animationOut, speed, function () {
  159. var $this = $(this);
  160. o.onHide.call($this);
  161. });
  162. }
  163. return this;
  164. },
  165. show: function () {
  166. var o = getOptions(this);
  167. if (!o) {
  168. return this;
  169. }
  170. var $this = this.addClass(o.hoverClass),
  171. $ul = $this.children(o.popUpSelector);
  172. if (o.onBeforeShow.call($ul) === false) {
  173. return this;
  174. }
  175. $ul.stop(true, true).animate(o.animation, o.speed, function () {
  176. o.onShow.call($ul);
  177. });
  178. return this;
  179. },
  180. destroy: function () {
  181. return this.each(function () {
  182. var $this = $(this),
  183. o = $this.data('sfOptions'),
  184. $hasPopUp;
  185. if (!o) {
  186. return false;
  187. }
  188. $hasPopUp = $this.find(o.popUpSelector).parent('li');
  189. clearTimeout(o.sfTimer);
  190. toggleMenuClasses($this, o);
  191. toggleAnchorClass($hasPopUp);
  192. toggleTouchAction($this);
  193. // remove event handlers
  194. $this.off('.superfish').off('.hoverIntent');
  195. // clear animation's inline display style
  196. $hasPopUp.children(o.popUpSelector).attr('style', function (i, style) {
  197. return style.replace(/display[^;]+;?/g, '');
  198. });
  199. // reset 'current' path classes
  200. o.$path.removeClass(o.hoverClass + ' ' + c.bcClass).addClass(o.pathClass);
  201. $this.find('.' + o.hoverClass).removeClass(o.hoverClass);
  202. o.onDestroy.call($this);
  203. $this.removeData('sfOptions');
  204. });
  205. },
  206. init: function (op) {
  207. return this.each(function () {
  208. var $this = $(this);
  209. if ($this.data('sfOptions')) {
  210. return false;
  211. }
  212. var o = $.extend({}, $.fn.superfish.defaults, op),
  213. $hasPopUp = $this.find(o.popUpSelector).parent('li');
  214. o.$path = setPathToCurrent($this, o);
  215. $this.data('sfOptions', o);
  216. toggleMenuClasses($this, o, true);
  217. toggleAnchorClass($hasPopUp, true);
  218. toggleTouchAction($this);
  219. applyHandlers($this, o);
  220. $hasPopUp.not('.' + c.bcClass).superfish('hide', true);
  221. o.onInit.call(this);
  222. });
  223. }
  224. };
  225. })();
  226. $.fn.superfish = function (method, args) {
  227. if (methods[method]) {
  228. return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  229. }
  230. else if (typeof method === 'object' || ! method) {
  231. return methods.init.apply(this, arguments);
  232. }
  233. else {
  234. return $.error('Method ' + method + ' does not exist on jQuery.fn.superfish');
  235. }
  236. };
  237. $.fn.superfish.defaults = {
  238. popUpSelector: 'ul,.sf-mega', // within menu context
  239. hoverClass: 'sfHover',
  240. pathClass: 'overrideThisToUse',
  241. pathLevels: 1,
  242. delay: 800,
  243. animation: {opacity: 'show'},
  244. animationOut: {opacity: 'hide'},
  245. speed: 'normal',
  246. speedOut: 'fast',
  247. cssArrows: true,
  248. disableHI: false,
  249. onInit: $.noop,
  250. onBeforeShow: $.noop,
  251. onShow: $.noop,
  252. onBeforeHide: $.noop,
  253. onHide: $.noop,
  254. onIdle: $.noop,
  255. onDestroy: $.noop,
  256. onHandleTouch: $.noop
  257. };
  258. })(jQuery, window);