/* 该文档由“写代码的平面师”整理 */ //duration——>持续的时间 //easing ——>运动方式(32种) jQuery.easing['jswing'] = jQuery.easing['swing']; jQuery.extend( jQuery.easing, { def: 'easeOutQuad', //慢速开始,中间变快,慢速结束 swing: function (x, t, b, c, d) { return jQuery.easing[jQuery.easing.def](x, t, b, c, d); }, //慢速开始,逐渐变快 easeInQuad: function (x, t, b, c, d) { return c*(t/=d)*t + b; }, //快速开始,逐渐变慢 easeOutQuad: function (x, t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, //慢速开始,中间变快,慢速结束 easeInOutQuad: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; }, easeInCubic: function (x, t, b, c, d) { return c*(t/=d)*t*t + b; }, easeOutCubic: function (x, t, b, c, d) { return c*((t=t/d-1)*t*t + 1) + b; }, easeInOutCubic: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; }, easeInQuart: function (x, t, b, c, d) { return c*(t/=d)*t*t*t + b; }, easeOutQuart: function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }, easeInOutQuart: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t*t + b; return -c/2 * ((t-=2)*t*t*t - 2) + b; }, easeInQuint: function (x, t, b, c, d) { return c*(t/=d)*t*t*t*t + b; }, easeOutQuint: function (x, t, b, c, d) { return c*((t=t/d-1)*t*t*t*t + 1) + b; }, easeInOutQuint: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; return c/2*((t-=2)*t*t*t*t + 2) + b; }, easeInSine: function (x, t, b, c, d) { return -c * Math.cos(t/d * (Math.PI/2)) + c + b; }, easeOutSine: function (x, t, b, c, d) { return c * Math.sin(t/d * (Math.PI/2)) + b; }, easeInOutSine: function (x, t, b, c, d) { return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; }, easeInExpo: function (x, t, b, c, d) { return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; }, easeOutExpo: function (x, t, b, c, d) { return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; }, easeInOutExpo: function (x, t, b, c, d) { if (t==0) return b; if (t==d) return b+c; if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; }, easeInCirc: function (x, t, b, c, d) { return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; }, easeOutCirc: function (x, t, b, c, d) { return c * Math.sqrt(1 - (t=t/d-1)*t) + b; }, easeInOutCirc: function (x, t, b, c, d) { if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; }, easeInElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; }, //结尾可伸缩的运动 easeOutElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; }, //开始与结尾都有伸缩运动 easeInOutElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; }, //开始具有微弱的伸缩运动 easeInBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; return c*(t/=d)*t*((s+1)*t - s) + b; }, //结尾具有微弱的伸缩运动 easeOutBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; }, //开始与结尾具有微弱的伸缩运动 easeInOutBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; }, //开始具有弹性运动 easeInBounce: function (x, t, b, c, d) { return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; }, //结尾具有弹性运动 easeOutBounce: function (x, t, b, c, d) { if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } }, //开始与结束均有弹性动画 easeInOutBounce: function (x, t, b, c, d) { if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; } }); // // SmoothScroll (function () { // Scroll Variables (tweakable) var defaultOptions = { // Scrolling Core frameRate : 150, // [Hz] animationTime : 400, // [ms] stepSize : 100, // [px] // Pulse (less tweakable) // ratio of "tail" to "acceleration" pulseAlgorithm : true, pulseScale : 4, pulseNormalize : 1, // Acceleration accelerationDelta : 2, // 50 accelerationMax : 2, // 3 // Keyboard Settings keyboardSupport : true, // option arrowScroll : 2, // [px] // Other fixedBackground : true, excluded : '' }; var options = defaultOptions; // Other Variables var isExcluded = false; var isFrame = false; var direction = { x: 0, y: 0 }; var initDone = false; var root = document.documentElement; var activeElement; var observer; var refreshSize; var deltaBuffer = []; var isMac = /^Mac/.test(navigator.platform); var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36 }; var arrowKeys = { 37: 1, 38: 1, 39: 1, 40: 1 }; /*********************************************** * INITIALIZE ***********************************************/ /** * Tests if smooth scrolling is allowed. Shuts down everything if not. */ function initTest() { if (options.keyboardSupport) { addEvent('keydown', keydown); } } /** * Sets up scrolls array, determines if frames are involved. */ function init() { if (initDone || !document.body) return; initDone = true; var body = document.body; var html = document.documentElement; var windowHeight = window.innerHeight; var scrollHeight = body.scrollHeight; // check compat mode for root element root = (document.compatMode.indexOf('CSS') >= 0) ? html : body; activeElement = body; initTest(); // Checks if this script is running in a frame if (top != self) { isFrame = true; } /** * Safari 10 fixed it, Chrome fixed it in v45: * This fixes a bug where the areas left and right to * the content does not trigger the onmousewheel event * on some pages. e.g.: html, body { height: 100% } */ else if (isOldSafari && scrollHeight > windowHeight && (body.offsetHeight <= windowHeight || html.offsetHeight <= windowHeight)) { var fullPageElem = document.createElement('div'); fullPageElem.style.cssText = 'position:absolute; z-index:-10000; ' + 'top:0; left:0; right:0; height:' + root.scrollHeight + 'px'; document.body.appendChild(fullPageElem); // DOM changed (throttled) to fix height var pendingRefresh; refreshSize = function () { if (pendingRefresh) return; // could also be: clearTimeout(pendingRefresh); pendingRefresh = setTimeout(function () { if (isExcluded) return; // could be running after cleanup fullPageElem.style.height = '0'; fullPageElem.style.height = root.scrollHeight + 'px'; pendingRefresh = null; }, 500); // act rarely to stay fast }; setTimeout(refreshSize, 10); addEvent('resize', refreshSize); // TODO: attributeFilter? var config = { attributes: true, childList: true, characterData: false // subtree: true }; observer = new MutationObserver(refreshSize); observer.observe(body, config); if (root.offsetHeight <= windowHeight) { var clearfix = document.createElement('div'); clearfix.style.clear = 'both'; body.appendChild(clearfix); } } // disable fixed background if (!options.fixedBackground && !isExcluded) { body.style.backgroundAttachment = 'scroll'; html.style.backgroundAttachment = 'scroll'; } } /** * Removes event listeners and other traces left on the page. */ function cleanup() { observer && observer.disconnect(); removeEvent(wheelEvent, wheel); removeEvent('mousedown', mousedown); removeEvent('keydown', keydown); removeEvent('resize', refreshSize); removeEvent('load', init); } /************************************************ * SCROLLING ************************************************/ var que = []; var pending = false; var lastScroll = Date.now(); /** * Pushes scroll actions to the scrolling queue. */ function scrollArray(elem, left, top) { directionCheck(left, top); if (options.accelerationMax != 1) { var now = Date.now(); var elapsed = now - lastScroll; if (elapsed < options.accelerationDelta) { var factor = (1 + (50 / elapsed)) / 2; if (factor > 1) { factor = Math.min(factor, options.accelerationMax); left *= factor; top *= factor; } } lastScroll = Date.now(); } // push a scroll command que.push({ x: left, y: top, lastX: (left < 0) ? 0.99 : -0.99, lastY: (top < 0) ? 0.99 : -0.99, start: Date.now() }); // don't act if there's a pending queue if (pending) { return; } var scrollWindow = (elem === document.body); var step = function (time) { var now = Date.now(); var scrollX = 0; var scrollY = 0; for (var i = 0; i < que.length; i++) { var item = que[i]; var elapsed = now - item.start; var finished = (elapsed >= options.animationTime); // scroll position: [0, 1] var position = (finished) ? 1 : elapsed / options.animationTime; // easing [optional] if (options.pulseAlgorithm) { position = pulse(position); } // only need the difference var x = (item.x * position - item.lastX) >> 0; var y = (item.y * position - item.lastY) >> 0; // add this to the total scrolling scrollX += x; scrollY += y; // update last values item.lastX += x; item.lastY += y; // delete and step back if it's over if (finished) { que.splice(i, 1); i--; } } // scroll left and top if (scrollWindow) { window.scrollBy(scrollX, scrollY); } else { if (scrollX) elem.scrollLeft += scrollX; if (scrollY) elem.scrollTop += scrollY; } // clean up if there's nothing left to do if (!left && !top) { que = []; } if (que.length) { requestFrame(step, elem, (1000 / options.frameRate + 1)); } else { pending = false; } }; // start a new queue of actions requestFrame(step, elem, 0); pending = true; } /*********************************************** * EVENTS ***********************************************/ /** * Mouse wheel handler. * @param {Object} event */ function wheel(event) { if (!initDone) { init(); } var target = event.target; // leave early if default action is prevented // or it's a zooming event with CTRL if (event.defaultPrevented || event.ctrlKey) { return true; } // leave embedded content alone (flash & pdf) if (isNodeName(activeElement, 'embed') || (isNodeName(target, 'embed') && /\.pdf/i.test(target.src)) || isNodeName(activeElement, 'object') || target.shadowRoot) { return true; } var deltaX = -event.wheelDeltaX || event.deltaX || 0; var deltaY = -event.wheelDeltaY || event.deltaY || 0; if (isMac) { if (event.wheelDeltaX && isDivisible(event.wheelDeltaX, 120)) { deltaX = -120 * (event.wheelDeltaX / Math.abs(event.wheelDeltaX)); } if (event.wheelDeltaY && isDivisible(event.wheelDeltaY, 120)) { deltaY = -120 * (event.wheelDeltaY / Math.abs(event.wheelDeltaY)); } } // use wheelDelta if deltaX/Y is not available if (!deltaX && !deltaY) { deltaY = -event.wheelDelta || 0; } // line based scrolling (Firefox mostly) if (event.deltaMode === 1) { deltaX *= 40; deltaY *= 40; } var overflowing = overflowingAncestor(target); // nothing to do if there's no element that's scrollable if (!overflowing) { // except Chrome iframes seem to eat wheel events, which we need to // propagate up, if the iframe has nothing overflowing to scroll if (isFrame && isChrome) { // change target to iframe element itself for the parent frame Object.defineProperty(event, "target", {value: window.frameElement}); return parent.wheel(event); } return true; } // check if it's a touchpad scroll that should be ignored if (isTouchpad(deltaY)) { return true; } // scale by step size // delta is 120 most of the time // synaptics seems to send 1 sometimes if (Math.abs(deltaX) > 1.2) { deltaX *= options.stepSize / 120; } if (Math.abs(deltaY) > 1.2) { deltaY *= options.stepSize / 120; } scrollArray(overflowing, deltaX, deltaY); //event.preventDefault(); scheduleClearCache(); } /** * Keydown event handler. * @param {Object} event */ function keydown(event) { var target = event.target; var modifier = event.ctrlKey || event.altKey || event.metaKey || (event.shiftKey && event.keyCode !== key.spacebar); // our own tracked active element could've been removed from the DOM if (!document.body.contains(activeElement)) { activeElement = document.activeElement; } // do nothing if user is editing text // or using a modifier key (except shift) // or in a dropdown // or inside interactive elements var inputNodeNames = /^(textarea|select|embed|object)$/i; var buttonTypes = /^(button|submit|radio|checkbox|file|color|image)$/i; if ( event.defaultPrevented || inputNodeNames.test(target.nodeName) || isNodeName(target, 'input') && !buttonTypes.test(target.type) || isNodeName(activeElement, 'video') || isInsideYoutubeVideo(event) || target.isContentEditable || modifier ) { return true; } // [spacebar] should trigger button press, leave it alone if ((isNodeName(target, 'button') || isNodeName(target, 'input') && buttonTypes.test(target.type)) && event.keyCode === key.spacebar) { return true; } // [arrwow keys] on radio buttons should be left alone if (isNodeName(target, 'input') && target.type == 'radio' && arrowKeys[event.keyCode]) { return true; } var shift, x = 0, y = 0; var overflowing = overflowingAncestor(activeElement); if (!overflowing) { // Chrome iframes seem to eat key events, which we need to // propagate up, if the iframe has nothing overflowing to scroll return (isFrame && isChrome) ? parent.keydown(event) : true; } var clientHeight = overflowing.clientHeight; if (overflowing == document.body) { clientHeight = window.innerHeight; } switch (event.keyCode) { case key.up: y = -options.arrowScroll; break; case key.down: y = options.arrowScroll; break; case key.spacebar: // (+ shift) shift = event.shiftKey ? 1 : -1; y = -shift * clientHeight * 0.9; break; case key.pageup: y = -clientHeight * 0.9; break; case key.pagedown: y = clientHeight * 0.9; break; case key.home: y = -overflowing.scrollTop; break; case key.end: var scroll = overflowing.scrollHeight - overflowing.scrollTop; var scrollRemaining = scroll - clientHeight; y = (scrollRemaining > 0) ? scrollRemaining + 10 : 0; break; case key.left: x = -options.arrowScroll; break; case key.right: x = options.arrowScroll; break; default: return true; // a key we don't care about } scrollArray(overflowing, x, y); event.preventDefault(); scheduleClearCache(); } /** * Mousedown event only for updating activeElement */ function mousedown(event) { activeElement = event.target; } /*********************************************** * OVERFLOW ***********************************************/ var uniqueID = (function () { var i = 0; return function (el) { return el.uniqueID || (el.uniqueID = i++); }; })(); var cache = {}; // cleared out after a scrolling session var clearCacheTimer; //setInterval(function () { cache = {}; }, 10 * 1000); function scheduleClearCache() { clearTimeout(clearCacheTimer); clearCacheTimer = setInterval(function () { cache = {}; }, 1*1000); } function setCache(elems, overflowing) { for (var i = elems.length; i--;) cache[uniqueID(elems[i])] = overflowing; return overflowing; } // (body) (root) // | hidden | visible | scroll | auto | // hidden | no | no | YES | YES | // visible | no | YES | YES | YES | // scroll | no | YES | YES | YES | // auto | no | YES | YES | YES | function overflowingAncestor(el) { var elems = []; var body = document.body; var rootScrollHeight = root.scrollHeight; do { var cached = cache[uniqueID(el)]; if (cached) { return setCache(elems, cached); } elems.push(el); if (rootScrollHeight === el.scrollHeight) { var topOverflowsNotHidden = overflowNotHidden(root) && overflowNotHidden(body); var isOverflowCSS = topOverflowsNotHidden || overflowAutoOrScroll(root); if (isFrame && isContentOverflowing(root) || !isFrame && isOverflowCSS) { return setCache(elems, getScrollRoot()); } } else if (isContentOverflowing(el) && overflowAutoOrScroll(el)) { return setCache(elems, el); } } while (el = el.parentElement); } function isContentOverflowing(el) { return (el.clientHeight + 10 < el.scrollHeight); } // typically for and function overflowNotHidden(el) { var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y'); return (overflow !== 'hidden'); } // for all other elements function overflowAutoOrScroll(el) { var overflow = getComputedStyle(el, '').getPropertyValue('overflow-y'); return (overflow === 'scroll' || overflow === 'auto'); } /*********************************************** * HELPERS ***********************************************/ function addEvent(type, fn) { window.addEventListener(type, fn, false); } function removeEvent(type, fn) { window.removeEventListener(type, fn, false); } function isNodeName(el, tag) { return (el.nodeName||'').toLowerCase() === tag.toLowerCase(); } function directionCheck(x, y) { x = (x > 0) ? 1 : -1; y = (y > 0) ? 1 : -1; if (direction.x !== x || direction.y !== y) { direction.x = x; direction.y = y; que = []; lastScroll = 0; } } var deltaBufferTimer; if (window.localStorage && localStorage.SS_deltaBuffer) { try { // #46 Safari throws in private browsing for localStorage deltaBuffer = localStorage.SS_deltaBuffer.split(','); } catch (e) { } } function isTouchpad(deltaY) { if (!deltaY) return; if (!deltaBuffer.length) { deltaBuffer = [deltaY, deltaY, deltaY]; } deltaY = Math.abs(deltaY); deltaBuffer.push(deltaY); deltaBuffer.shift(); clearTimeout(deltaBufferTimer); deltaBufferTimer = setTimeout(function () { try { // #46 Safari throws in private browsing for localStorage localStorage.SS_deltaBuffer = deltaBuffer.join(','); } catch (e) { } }, 1000); return !allDeltasDivisableBy(120) && !allDeltasDivisableBy(100); } function isDivisible(n, divisor) { return (Math.floor(n / divisor) == n / divisor); } function allDeltasDivisableBy(divisor) { return (isDivisible(deltaBuffer[0], divisor) && isDivisible(deltaBuffer[1], divisor) && isDivisible(deltaBuffer[2], divisor)); } function isInsideYoutubeVideo(event) { var elem = event.target; var isControl = false; if (document.URL.indexOf ('www.youtube.com/watch') != -1) { do { isControl = (elem.classList && elem.classList.contains('html5-video-controls')); if (isControl) break; } while (elem = elem.parentNode); } return isControl; } var requestFrame = (function () { return (window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback, element, delay) { window.setTimeout(callback, delay || (1000/60)); }); })(); var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver); var getScrollRoot = (function() { var SCROLL_ROOT; return function() { if (!SCROLL_ROOT) { var dummy = document.createElement('div'); dummy.style.cssText = 'height:10000px;width:1px;'; document.body.appendChild(dummy); var bodyScrollTop = document.body.scrollTop; var docElScrollTop = document.documentElement.scrollTop; window.scrollBy(0, 3); if (document.body.scrollTop != bodyScrollTop) (SCROLL_ROOT = document.body); else (SCROLL_ROOT = document.documentElement); window.scrollBy(0, -3); document.body.removeChild(dummy); } return SCROLL_ROOT; }; })(); /*********************************************** * PULSE (by Michael Herf) ***********************************************/ /** * Viscous fluid with a pulse for part and decay for the rest. * - Applies a fixed force over an interval (a damped acceleration), and * - Lets the exponential bleed away the velocity over a longer interval * - Michael Herf, http://stereopsis.com/stopping/ */ function pulse_(x) { var val, start, expx; // test x = x * options.pulseScale; if (x < 1) { // acceleartion val = x - (1 - Math.exp(-x)); } else { // tail // the previous animation ended here: start = Math.exp(-1); // simple viscous drag x -= 1; expx = 1 - Math.exp(-x); val = start + (expx * (1 - start)); } return val * options.pulseNormalize; } function pulse(x) { if (x >= 1) return 1; if (x <= 0) return 0; if (options.pulseNormalize == 1) { options.pulseNormalize /= pulse_(1); } return pulse_(x); } /*********************************************** * FIRST RUN ***********************************************/ var userAgent = window.navigator.userAgent; var isEdge = /Edge/.test(userAgent); // thank you MS var isChrome = /chrome/i.test(userAgent) && !isEdge; var isSafari = /safari/i.test(userAgent) && !isEdge; var isMobile = /mobile/i.test(userAgent); var isIEWin7 = /Windows NT 6.1/i.test(userAgent) && /rv:11/i.test(userAgent); var isOldSafari = isSafari && (/Version\/8/i.test(userAgent) || /Version\/9/i.test(userAgent)); var isEnabledForBrowser = (isChrome || isSafari || isIEWin7) && !isMobile; var wheelEvent; if ('onwheel' in document.createElement('div')) wheelEvent = 'wheel'; else if ('onmousewheel' in document.createElement('div')) wheelEvent = 'mousewheel'; if (wheelEvent && isEnabledForBrowser) { addEvent(wheelEvent, wheel); addEvent('mousedown', mousedown); addEvent('load', init); } /*********************************************** * PUBLIC INTERFACE ***********************************************/ function SmoothScroll(optionsToSet) { for (var key in optionsToSet) if (defaultOptions.hasOwnProperty(key)) options[key] = optionsToSet[key]; } SmoothScroll.destroy = cleanup; if (window.SmoothScrollOptions) // async API SmoothScroll(window.SmoothScrollOptions); if (typeof define === 'function' && define.amd) define(function() { return SmoothScroll; }); else if ('object' == typeof exports) module.exports = SmoothScroll; else window.SmoothScroll = SmoothScroll; })(); /* * jQuery mmenu v4.1.1 * @requires jQuery 1.7.0 or later * * mmenu.frebsite.nl * * Copyright (c) Fred Heusschen * www.frebsite.nl * * Dual licensed under the MIT and GPL licenses. * http://en.wikipedia.org/wiki/MIT_License * http://en.wikipedia.org/wiki/GNU_General_Public_License */ (function( $ ) { var _PLUGIN_ = 'mmenu', _VERSION_ = '4.1.1'; // Plugin already excists if ( $[ _PLUGIN_ ] ) { return; } // Global variables var glbl = { $wndw: null, $html: null, $body: null, $page: null, $blck: null, $allMenus: null, $scrollTopNode: null }; var _c = {}, _e = {}, _d = {}, _serialnr = 0; $[ _PLUGIN_ ] = function( $menu, opts, conf ) { glbl.$allMenus = glbl.$allMenus.add( $menu ); this.$menu = $menu; this.opts = opts this.conf = conf; this.serialnr = _serialnr++; this._init(); return this; }; $[ _PLUGIN_ ].prototype = { open: function() { this._openSetup(); this._openFinish(); return 'open'; }, _openSetup: function() { // Find scrolltop var _scrollTop = findScrollTop(); // Set opened this.$menu.addClass( _c.current ); // Close others glbl.$allMenus.not( this.$menu ).trigger( _e.close ); // Store style and position glbl.$page .data( _d.style, glbl.$page.attr( 'style' ) || '' ) .data( _d.scrollTop, _scrollTop ) .data( _d.offetLeft, glbl.$page.offset().left ); // Resize page to window width var _w = 0; glbl.$wndw.off( _e.resize ) .on( _e.resize, function( e, force ) { if ( glbl.$html.hasClass( _c.opened ) || force ) { var nw = glbl.$wndw.width(); if ( nw != _w ) { _w = nw; glbl.$page.width( nw - glbl.$page.data( _d.offetLeft ) ); } } } ) .trigger( _e.resize, [ true ] ); // Prevent tabbing out of the menu if ( this.conf.preventTabbing ) { glbl.$wndw.off( _e.keydown ) .on( _e.keydown, function( e ) { if ( e.keyCode == 9 ) { e.preventDefault(); return false; } } ); } // Add options if ( this.opts.modal ) { glbl.$html.addClass( _c.modal ); } if ( this.opts.moveBackground ) { glbl.$html.addClass( _c.background ); } if ( this.opts.position != 'left' ) { glbl.$html.addClass( _c.mm( this.opts.position ) ); } if ( this.opts.zposition != 'back' ) { glbl.$html.addClass( _c.mm( this.opts.zposition ) ); } if ( this.opts.classes ) { glbl.$html.addClass( this.opts.classes ); } // Open glbl.$html.addClass( _c.opened ); this.$menu.addClass( _c.opened ); // Scroll page to scrolltop glbl.$page.scrollTop( _scrollTop ); // Scroll menu to top this.$menu.scrollTop( 0 ); }, _openFinish: function() { var that = this; // Callback transitionend( glbl.$page, function() { that.$menu.trigger( _e.opened ); }, this.conf.transitionDuration ); // Opening glbl.$html.addClass( _c.opening ); this.$menu.trigger( _e.opening ); // Scroll window to top window.scrollTo( 0, 1 ); }, close: function() { var that = this; // Callback transitionend( glbl.$page, function() { that.$menu .removeClass( _c.current ) .removeClass( _c.opened ); glbl.$html .removeClass( _c.opened ) .removeClass( _c.modal ) .removeClass( _c.background ) .removeClass( _c.mm( that.opts.position ) ) .removeClass( _c.mm( that.opts.zposition ) ); if ( that.opts.classes ) { glbl.$html.removeClass( that.opts.classes ); } glbl.$wndw .off( _e.resize ) .off( _e.keydown ); // Restore style and position glbl.$page.attr( 'style', glbl.$page.data( _d.style ) ); if ( glbl.$scrollTopNode ) { glbl.$scrollTopNode.scrollTop( glbl.$page.data( _d.scrollTop ) ); } // Closed that.$menu.trigger( _e.closed ); }, this.conf.transitionDuration ); // Closing glbl.$html.removeClass( _c.opening ); this.$menu.trigger( _e.closing ); return 'close'; }, _init: function() { this.opts = extendOptions( this.opts, this.conf, this.$menu ); this.direction = ( this.opts.slidingSubmenus ) ? 'horizontal' : 'vertical'; // INIT PAGE & MENU this._initPage( glbl.$page ); this._initMenu(); this._initBlocker(); this._initPanles(); this._initLinks(); this._initOpenClose(); this._bindCustomEvents(); if ( $[ _PLUGIN_ ].addons ) { for ( var a = 0; a < $[ _PLUGIN_ ].addons.length; a++ ) { if ( typeof this[ '_addon_' + $[ _PLUGIN_ ].addons[ a ] ] == 'function' ) { this[ '_addon_' + $[ _PLUGIN_ ].addons[ a ] ](); } } } }, _bindCustomEvents: function() { var that = this; this.$menu .off( _e.open + ' ' + _e.close + ' ' + _e.setPage+ ' ' + _e.update ) .on( _e.open + ' ' + _e.close + ' ' + _e.setPage+ ' ' + _e.update, function( e ) { e.stopPropagation(); } ); // Menu-events this.$menu .on( _e.open, function( e ) { if ( $(this).hasClass( _c.current ) ) { e.stopImmediatePropagation(); return false; } return that.open(); } ) .on( _e.close, function( e ) { if ( !$(this).hasClass( _c.current ) ) { e.stopImmediatePropagation(); return false; } return that.close(); } ) .on( _e.setPage, function( e, $p ) { that._initPage( $p ); that._initOpenClose(); } ); // Panel-events var $panels = this.$menu.find( this.opts.isMenu && this.direction != 'horizontal' ? 'ul, ol' : '.' + _c.panel ); $panels .off( _e.toggle + ' ' + _e.open + ' ' + _e.close ) .on( _e.toggle + ' ' + _e.open + ' ' + _e.close, function( e ) { e.stopPropagation(); } ); if ( this.direction == 'horizontal' ) { $panels .on( _e.open, function( e ) { return openSubmenuHorizontal( $(this), that.$menu ); } ); } else { $panels .on( _e.toggle, function( e ) { var $t = $(this); return $t.triggerHandler( $t.parent().hasClass( _c.opened ) ? _e.close : _e.open ); } ) .on( _e.open, function( e ) { $(this).parent().addClass( _c.opened ); return 'open'; } ) .on( _e.close, function( e ) { $(this).parent().removeClass( _c.opened ); return 'close'; } ); } }, _initBlocker: function() { var that = this; if ( !glbl.$blck ) { glbl.$blck = $( '
' ).appendTo( glbl.$body ); } click( glbl.$blck, function() { if ( !glbl.$html.hasClass( _c.modal ) ) { that.$menu.trigger( _e.close ); } }, true, true ); }, _initPage: function( $p ) { if ( !$p ) { $p = $(this.conf.pageSelector, glbl.$body); if ( $p.length > 1 ) { $[ _PLUGIN_ ].debug( 'Multiple nodes found for the page-node, all nodes are wrapped in one <' + this.conf.pageNodetype + '>.' ); $p = $p.wrapAll( '<' + this.conf.pageNodetype + ' />' ).parent(); } } $p.addClass( _c.page ); glbl.$page = $p; }, _initMenu: function() { var that = this; // Clone if needed if ( this.conf.clone ) { this.$menu = this.$menu.clone( true ); this.$menu.add( this.$menu.find( '*' ) ).filter( '[id]' ).each( function() { $(this).attr( 'id', _c.mm( $(this).attr( 'id' ) ) ); } ); } // Strip whitespace this.$menu.contents().each( function() { if ( $(this)[ 0 ].nodeType == 3 ) { $(this).remove(); } } ); // Prepend to body this.$menu .prependTo( 'body' ) .addClass( _c.menu ); // Add direction class this.$menu.addClass( _c.mm( this.direction ) ); // Add options classes if ( this.opts.classes ) { this.$menu.addClass( this.opts.classes ); } if ( this.opts.isMenu ) { this.$menu.addClass( _c.ismenu ); } if ( this.opts.position != 'left' ) { this.$menu.addClass( _c.mm( this.opts.position ) ); } if ( this.opts.zposition != 'back' ) { this.$menu.addClass( _c.mm( this.opts.zposition ) ); } }, _initPanles: function() { var that = this; // Refactor List class this.__refactorClass( $('.' + this.conf.listClass, this.$menu), 'list' ); // Add List class if ( this.opts.isMenu ) { $('ul, ol', this.$menu) .not( '.mm-nolist' ) .addClass( _c.list ); } var $lis = $('.' + _c.list + ' > li', this.$menu); // Refactor Selected class this.__refactorClass( $lis.filter( '.' + this.conf.selectedClass ), 'selected' ); // Refactor Label class this.__refactorClass( $lis.filter( '.' + this.conf.labelClass ), 'label' ); // Refactor Spacer class this.__refactorClass( $lis.filter( '.' + this.conf.spacerClass ), 'spacer' ); // setSelected-event $lis .off( _e.setSelected ) .on( _e.setSelected, function( e, selected ) { e.stopPropagation(); $lis.removeClass( _c.selected ); if ( typeof selected != 'boolean' ) { selected = true; } if ( selected ) { $(this).addClass( _c.selected ); } } ); // Refactor Panel class this.__refactorClass( $('.' + this.conf.panelClass, this.$menu), 'panel' ); // Add Panel class this.$menu .children() .filter( this.conf.panelNodetype ) .add( this.$menu.find( '.' + _c.list ).children().children().filter( this.conf.panelNodetype ) ) .addClass( _c.panel ); var $panels = $('.' + _c.panel, this.$menu); // Add an ID to all panels $panels .each( function( i ) { var $t = $(this), id = $t.attr( 'id' ) || _c.mm( 'm' + that.serialnr + '-p' + i ); $t.attr( 'id', id ); } ); // Add open and close links to menu items $panels .find( '.' + _c.panel ) .each( function( i ) { var $t = $(this), $u = $t.is( 'ul, ol' ) ? $t : $t.find( 'ul ,ol' ).first(), $l = $t.parent(), $a = $l.find( '> a, > span' ), $p = $l.closest( '.' + _c.panel ); $t.data( _d.parent, $l ); if ( $l.parent().is( '.' + _c.list ) ) { var $btn = $( '' ).insertBefore( $a ); if ( !$a.is( 'a' ) ) { $btn.addClass( _c.fullsubopen ); } if ( that.direction == 'horizontal' ) { $u.prepend( '
  • ' + $a.text() + '
  • ' ); } } } ); // Link anchors to panels var evt = this.direction == 'horizontal' ? _e.open : _e.toggle; $panels .each( function( i ) { var $opening = $(this), id = $opening.attr( 'id' ); click( $('a[href="#' + id + '"]', that.$menu), function( e ) { $opening.trigger( evt ); } ); } ); if ( this.direction == 'horizontal' ) { // Add opened-classes var $selected = $('.' + _c.list + ' > li.' + _c.selected, this.$menu); $selected .add( $selected.parents( 'li' ) ) .parents( 'li' ).removeClass( _c.selected ) .end().each( function() { var $t = $(this), $u = $t.find( '> .' + _c.panel ); if ( $u.length ) { $t.parents( '.' + _c.panel ).addClass( _c.subopened ); $u.addClass( _c.opened ); } } ) .closest( '.' + _c.panel ).addClass( _c.opened ) .parents( '.' + _c.panel ).addClass( _c.subopened ); } else { // Replace Selected-class with opened-class in parents from .Selected $('li.' + _c.selected, this.$menu) .addClass( _c.opened ) .parents( '.' + _c.selected ).removeClass( _c.selected ); } // Set current opened var $current = $panels.filter( '.' + _c.opened ); if ( !$current.length ) { $current = $panels.first(); } $current .addClass( _c.opened ) .last() .addClass( _c.current ); // Rearrange markup if ( this.direction == 'horizontal' ) { $panels.find( '.' + _c.panel ).appendTo( this.$menu ); } }, _initLinks: function() { var that = this; var $a = $('.' + _c.list + ' > li > a', this.$menu) .not( '.' + _c.subopen ) .not( '.' + _c.subclose ) .not( '[rel="external"]' ) .not( '[target="_blank"]' ); $a.off( _e.click ) .on( _e.click, function( e ) { var $t = $(this), href = $t.attr( 'href' ); // Set selected item if ( that.__valueOrFn( that.opts.onClick.setSelected, $t ) ) { $t.parent().trigger( _e.setSelected ); } // Prevent default / don't follow link. Default: false var preventDefault = that.__valueOrFn( that.opts.onClick.preventDefault, $t, href.slice( 0, 1 ) == '#' ); if ( preventDefault ) { e.preventDefault(); e.stopPropagation(); } // Block UI. Default: false if preventDefault, true otherwise if ( that.__valueOrFn( that.opts.onClick.blockUI, $t, !preventDefault ) ) { glbl.$html.addClass( _c.blocking ); } // Close menu. Default: true if preventDefault, false otherwise if ( that.__valueOrFn( that.opts.onClick.close, $t, preventDefault ) ) { that.$menu.triggerHandler( _e.close ); } } ); }, _initOpenClose: function() { var that = this; // Toggle menu var id = this.$menu.attr( 'id' ); if ( id && id.length ) { if ( this.conf.clone ) { id = _c.umm( id ); } click( $('a[href="#' + id + '"]'), function() { that.$menu.trigger( _e.open ); } ); } // Close menu var id = glbl.$page.attr( 'id' ); if ( id && id.length ) { click( $('a[href="#' + id + '"]'), function() { that.$menu.trigger( _e.close ); }, false, true ); } }, __valueOrFn: function( o, $e, d ) { if ( typeof o == 'function' ) { return o.call( $e[ 0 ] ); } if ( typeof o == 'undefined' && typeof d != 'undefined' ) { return d; } return o; }, __refactorClass: function( $e, c ) { $e.removeClass( this.conf[ c + 'Class' ] ).addClass( _c[ c ] ); } }; $.fn[ _PLUGIN_ ] = function( opts, conf ) { // First time plugin is fired if ( !glbl.$wndw ) { _initPlugin(); } // Extend options opts = extendOptions( opts, conf ); conf = extendConfiguration( conf ); return this.each( function() { var $menu = $(this); if ( $menu.data( _PLUGIN_ ) ) { return; } $menu.data( _PLUGIN_, new $[ _PLUGIN_ ]( $menu, opts, conf ) ); } ); }; $[ _PLUGIN_ ].version = _VERSION_; $[ _PLUGIN_ ].defaults = { position : 'left', zposition : 'back', moveBackground : true, slidingSubmenus : true, modal : false, classes : '', onClick : { // close : true, // blockUI : null, // preventDefault : null, setSelected : true } }; $[ _PLUGIN_ ].configuration = { preventTabbing : true, panelClass : 'Panel', listClass : 'List', selectedClass : 'Selected', labelClass : 'Label', spacerClass : 'Spacer', pageNodetype : 'div', panelNodetype : 'ul, ol, div', transitionDuration : 400 }; /* SUPPORT */ (function() { var wd = window.document, ua = window.navigator.userAgent; var _touch = 'ontouchstart' in wd, _overflowscrolling = 'WebkitOverflowScrolling' in wd.documentElement.style, _transition = (function() { var s = document.createElement( 'div' ).style; if ( 'webkitTransition' in s ) { return 'webkitTransition'; } return 'transition' in s; })(), _oldAndroidBrowser = (function() { if ( ua.indexOf( 'Android' ) >= 0 ) { return 2.4 > parseFloat( ua.slice( ua.indexOf( 'Android' ) +8 ) ); } return false; })(); $[ _PLUGIN_ ].support = { touch: _touch, transition: _transition, oldAndroidBrowser: _oldAndroidBrowser, overflowscrolling: (function() { if ( !_touch ) { return true; } if ( _overflowscrolling ) { return true; } if ( _oldAndroidBrowser ) { return false; } return true; })() }; })(); /* BROWSER SPECIFIC FIXES */ $[ _PLUGIN_ ].useOverflowScrollingFallback = function( use ) { if ( glbl.$html ) { if ( typeof use == 'boolean' ) { glbl.$html[ use ? 'addClass' : 'removeClass' ]( _c.nooverflowscrolling ); } return glbl.$html.hasClass( _c.nooverflowscrolling ); } else { _useOverflowScrollingFallback = use; return use; } }; /* DEBUG */ $[ _PLUGIN_ ].debug = function( msg ) {}; $[ _PLUGIN_ ].deprecated = function( depr, repl ) { if ( typeof console != 'undefined' && typeof console.warn != 'undefined' ) { console.warn( 'MMENU: ' + depr + ' is deprecated, use ' + repl + ' instead.' ); } }; // Global vars var _useOverflowScrollingFallback = !$[ _PLUGIN_ ].support.overflowscrolling; function extendOptions( o, c, $m ) { if ( typeof o != 'object' ) { o = {}; } if ( $m ) { if ( typeof o.isMenu != 'boolean' ) { var $c = $m.children(); o.isMenu = ( $c.length == 1 && $c.is( c.panelNodetype ) ); } return o; } // Extend onClick if ( typeof o.onClick != 'object' ) { o.onClick = {}; } // DEPRECATED if ( typeof o.onClick.setLocationHref != 'undefined' ) { $[ _PLUGIN_ ].deprecated( 'onClick.setLocationHref option', '!onClick.preventDefault' ); if ( typeof o.onClick.setLocationHref == 'boolean' ) { o.onClick.preventDefault = !o.onClick.setLocationHref; } } // /DEPRECATED // Extend from defaults o = $.extend( true, {}, $[ _PLUGIN_ ].defaults, o ); // Degration if ( $[ _PLUGIN_ ].useOverflowScrollingFallback() ) { switch( o.position ) { case 'top': case 'right': case 'bottom': $[ _PLUGIN_ ].debug( 'position: "' + o.position + '" not supported when using the overflowScrolling-fallback.' ); o.position = 'left'; break; } switch( o.zposition ) { case 'front': case 'next': $[ _PLUGIN_ ].debug( 'z-position: "' + o.zposition + '" not supported when using the overflowScrolling-fallback.' ); o.zposition = 'back'; break; } } return o; } function extendConfiguration( c ) { if ( typeof c != 'object' ) { c = {}; } // DEPRECATED if ( typeof c.panelNodeType != 'undefined' ) { $[ _PLUGIN_ ].deprecated( 'panelNodeType configuration option', 'panelNodetype' ); c.panelNodetype = c.panelNodeType; } // /DEPRECATED c = $.extend( true, {}, $[ _PLUGIN_ ].configuration, c ) // Set pageSelector if ( typeof c.pageSelector != 'string' ) { c.pageSelector = '> ' + c.pageNodetype; } return c; } function _initPlugin() { glbl.$wndw = $(window); glbl.$html = $('html'); glbl.$body = $('body'); glbl.$allMenus = $(); // Classnames, Datanames, Eventnames $.each( [ _c, _d, _e ], function( i, o ) { o.add = function( c ) { c = c.split( ' ' ); for ( var d in c ) { o[ c[ d ] ] = o.mm( c[ d ] ); } }; } ); // Classnames _c.mm = function( c ) { return 'mm-' + c; }; _c.add( 'menu ismenu panel list subtitle selected label spacer current highest hidden page blocker modal background opened opening subopened subopen fullsubopen subclose nooverflowscrolling' ); _c.umm = function( c ) { if ( c.slice( 0, 3 ) == 'mm-' ) { c = c.slice( 3 ); } return c; }; // Datanames _d.mm = function( d ) { return 'mm-' + d; }; _d.add( 'parent style scrollTop offetLeft' ); // Eventnames _e.mm = function( e ) { return e + '.mm'; }; _e.add( 'toggle open opening opened close closing closed update setPage setSelected transitionend touchstart touchend click keydown keyup resize' ); if ( !$[ _PLUGIN_ ].support.touch ) { _e.touchstart = _e.mm( 'mousedown' ); _e.touchend = _e.mm( 'mouseup' ); } $[ _PLUGIN_ ]._c = _c; $[ _PLUGIN_ ]._d = _d; $[ _PLUGIN_ ]._e = _e; $[ _PLUGIN_ ].glbl = glbl; $[ _PLUGIN_ ].useOverflowScrollingFallback( _useOverflowScrollingFallback ); } function openSubmenuHorizontal( $opening, $m ) { if ( $opening.hasClass( _c.current ) ) { return false; } var $panels = $('.' + _c.panel, $m), $current = $panels.filter( '.' + _c.current ); $panels .removeClass( _c.highest ) .removeClass( _c.current ) .not( $opening ) .not( $current ) .addClass( _c.hidden ); if ( $opening.hasClass( _c.opened ) ) { $current .addClass( _c.highest ) .removeClass( _c.opened ) .removeClass( _c.subopened ); } else { $opening .addClass( _c.highest ); $current .addClass( _c.subopened ); } $opening .removeClass( _c.hidden ) .removeClass( _c.subopened ) .addClass( _c.current ) .addClass( _c.opened ); return 'open'; } function findScrollTop() { if ( !glbl.$scrollTopNode ) { if ( glbl.$html.scrollTop() != 0 ) { glbl.$scrollTopNode = glbl.$html; } else if ( glbl.$body.scrollTop() != 0 ) { glbl.$scrollTopNode = glbl.$body; } } return ( glbl.$scrollTopNode ) ? glbl.$scrollTopNode.scrollTop() : 0; } function transitionend( $e, fn, duration ) { var s = $[ _PLUGIN_ ].support.transition; if ( s == 'webkitTransition' ) { $e.one( 'webkitTransitionEnd', fn ); } else if ( s ) { $e.one( _e.transitionend, fn ); } else { setTimeout( fn, duration ); } } function click( $b, fn, onTouchStart, add ) { if ( typeof $b == 'string' ) { $b = $( $b ); } var event = ( onTouchStart ) ? _e.touchstart : _e.click; if ( !add ) { $b.off( event ); } $b.on( event, function( e ) { e.preventDefault(); e.stopPropagation(); fn.call( this, e ); } ); } })( jQuery );