/** * SMFtooltip, Basic JQuery function to provide styled tooltips * * - will use the hoverintent plugin if available * - shows the tooltip in a div with the class defined in tooltipClass * - moves all selector titles to a hidden div and removes the title attribute to * prevent any default browser actions * - attempts to keep the tooltip on screen * * Simple Machines Forum (SMF) * * @package SMF * @author Simple Machines https://www.simplemachines.org * @copyright 2022 Simple Machines and individual contributors * @license https://www.simplemachines.org/about/smf/license.php BSD * * @version 2.1.0 * */ (function($) { $.fn.SMFtooltip = function(oInstanceSettings) { $.fn.SMFtooltip.oDefaultsSettings = { followMouse: 1, hoverIntent: {sensitivity: 10, interval: 300, timeout: 50}, positionTop: 12, positionLeft: 12, tooltipID: 'smf_tooltip', // ID used on the outer div tooltipTextID: 'smf_tooltipText', // as above but on the inner div holding the text tooltipClass: 'tooltip', // The class applied to the outer div (that displays on hover), use this in your css tooltipSwapClass: 'smf_swaptip', // a class only used internally, change only if you have a conflict tooltipContent: 'html' // display captured title text as html or text }; // account for any user options var oSettings = $.extend({}, $.fn.SMFtooltip.oDefaultsSettings , oInstanceSettings || {}); // move passed selector titles to a hidden span, then remove the selector title to prevent any default browser actions $(this).each(function() { var sTitle = $('' + htmlspecialchars(this.title) + '').hide(); $(this).append(sTitle).attr('title', ''); }); // determine where we are going to place the tooltip, while trying to keep it on screen var positionTooltip = function(event) { var iPosx = 0; var iPosy = 0; if (!event) var event = window.event; if (event.pageX || event.pageY) { iPosx = event.pageX; iPosy = event.pageY; } else if (event.clientX || event.clientY) { iPosx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; iPosy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; } // Position of the tooltip top left corner and its size var oPosition = { x: iPosx + oSettings.positionLeft, y: iPosy + oSettings.positionTop, w: $('#' + oSettings.tooltipID).width(), h: $('#' + oSettings.tooltipID).height() } // Display limits and window scroll postion var oLimits = { x: $(window).scrollLeft(), y: $(window).scrollTop(), w: $(window).width() - 24, h: $(window).height() - 24 }; // don't go off screen with our tooltop if ((oPosition.y + oPosition.h > oLimits.y + oLimits.h) && (oPosition.x + oPosition.w > oLimits.x + oLimits.w)) { oPosition.x = (oPosition.x - oPosition.w) - 45; oPosition.y = (oPosition.y - oPosition.h) - 45; } else if ((oPosition.x + oPosition.w) > (oLimits.x + oLimits.w)) { oPosition.x = oPosition.x - (((oPosition.x + oPosition.w) - (oLimits.x + oLimits.w)) + 24); } else if (oPosition.y + oPosition.h > oLimits.y + oLimits.h) { oPosition.y = oPosition.y - (((oPosition.y + oPosition.h) - (oLimits.y + oLimits.h)) + 24); } // finally set the position we determined $('#' + oSettings.tooltipID).css({'left': oPosition.x + 'px', 'top': oPosition.y + 'px'}); } // used to show a tooltip var showTooltip = function(){ $('#' + oSettings.tooltipID + ' #' + oSettings.tooltipTextID).show(); } // used to hide a tooltip var hideTooltip = function(valueOfThis){ $('#' + oSettings.tooltipID).fadeOut('slow').trigger("unload").remove(); } // used to keep html encoded function htmlspecialchars(string) { return $('').text(string).html(); } // for all of the elements that match the selector on the page, lets set up some actions return this.each(function(index) { // if we find hoverIntent use it if ($.fn.hoverIntent) { $(this).hoverIntent({ sensitivity: oSettings.hoverIntent.sensitivity, interval: oSettings.hoverIntent.interval, over: smf_tooltip_on, timeout: oSettings.hoverIntent.timeout, out: smf_tooltip_off }); } else { // plain old hover it is $(this).hover(smf_tooltip_on, smf_tooltip_off); } // create the on tip action function smf_tooltip_on(event) { // If we have text in the hidden span element we created on page load if ($(this).children('.' + oSettings.tooltipSwapClass).text()) { // create a ID'ed div with our style class that holds the tooltip info, hidden for now $('body').append('
'); // load information in to our newly created div var tt = $('#' + oSettings.tooltipID); var ttContent = $('#' + oSettings.tooltipID + ' #' + oSettings.tooltipTextID); if (oSettings.tooltipContent == 'html') ttContent.html($(this).children('.' + oSettings.tooltipSwapClass).html()); else ttContent.text($(this).children('.' + oSettings.tooltipSwapClass).text()); // show, then position, or it may position off screen tt.show(); showTooltip(); positionTooltip(event); } return false; }; // create the Bye bye tip function smf_tooltip_off(event) { hideTooltip(this); return false; }; // create the tip move with the cursor if (oSettings.followMouse) { $(this).on("mousemove", function(event){ positionTooltip(event); return false; }); } // clear the tip on a click $(this).on("click", function(event){ hideTooltip(this); return true; }); }); }; // A simple plugin for deleting an element from the DOM. $.fn.fadeOutAndRemove = function(speed){ if (typeof speed === 'undefined') speed = 400; $(this).fadeOut(speed,function(){ $(this).remove(); }); }; // Range to percent. $.fn.rangeToPercent = function(number, min, max){ return ((number - min) / (max - min)); }; // Percent to range. $.fn.percentToRange = function(percent, min, max){ return((max - min) * percent + min); }; })(jQuery); /** * AnimaDrag * Animated jQuery Drag and Drop Plugin * Version 0.5.1 beta * Author Abel Mohler * Released with the MIT License: https://opensource.org/licenses/mit-license.php */ (function($){ $.fn.animaDrag = function(o, callback) { var defaults = { speed: 400, interval: 300, easing: null, cursor: 'move', boundary: document.body, grip: null, overlay: true, after: function(e) {}, during: function(e) {}, before: function(e) {}, afterEachAnimation: function(e) {} } if(typeof callback == 'function') { defaults.after = callback; } o = $.extend(defaults, o || {}); return this.each(function() { var id, startX, startY, draggableStartX, draggableStartY, dragging = false, Ev, draggable = this, grip = ($(this).find(o.grip).length > 0) ? $(this).find(o.grip) : $(this); if(o.boundary) { var limitTop = $(o.boundary).offset().top, limitLeft = $(o.boundary).offset().left, limitBottom = limitTop + $(o.boundary).innerHeight(), limitRight = limitLeft + $(o.boundary).innerWidth(); } grip.mousedown(function(e) { o.before.call(draggable, e); var lastX, lastY; dragging = true; Ev = e; startX = lastX = e.pageX; startY = lastY = e.pageY; draggableStartX = $(draggable).offset().left; draggableStartY = $(draggable).offset().top; $(draggable).css({ position: 'absolute', left: draggableStartX + 'px', top: draggableStartY + 'px', cursor: o.cursor, zIndex: '1010' }).addClass('anima-drag').appendTo(document.body); if(o.overlay && $('#anima-drag-overlay').length == 0) { $('
').css({ position: 'absolute', top: '0', left: '0', zIndex: '1000', width: $(document.body).outerWidth() + 'px', height: $(document.body).outerHeight() + 'px' }).appendTo(document.body); } else if(o.overlay) { $('#anima-drag-overlay').show(); } id = setInterval(function() { if(lastX != Ev.pageX || lastY != Ev.pageY) { var positionX = draggableStartX - (startX - Ev.pageX), positionY = draggableStartY - (startY - Ev.pageY); if(positionX < limitLeft && o.boundary) { positionX = limitLeft; } else if(positionX + $(draggable).innerWidth() > limitRight && o.boundary) { positionX = limitRight - $(draggable).outerWidth(); } if(positionY < limitTop && o.boundary) { positionY = limitTop; } else if(positionY + $(draggable).innerHeight() > limitBottom && o.boundary) { positionY = limitBottom - $(draggable).outerHeight(); } $(draggable).stop().animate({ left: positionX + 'px', top: positionY + 'px' }, o.speed, o.easing, function(){o.afterEachAnimation.call(draggable, Ev)}); } lastX = Ev.pageX; lastY = Ev.pageY; }, o.interval); ($.browser.safari || e.preventDefault()); }); $(document).mousemove(function(e) { if(dragging) { Ev = e; o.during.call(draggable, e); } }); $(document).mouseup(function(e) { if(dragging) { $(draggable).css({ cursor: '', zIndex: '990' }).removeClass('anima-drag'); $('#anima-drag-overlay').hide().appendTo(document.body); clearInterval(id); o.after.call(draggable, e); dragging = false; } }); }); } })(jQuery); /* * jQuery Superfish Menu Plugin - v1.7.7 * Copyright (c) 2015 * * Dual licensed under the MIT and GPL licenses: * https://opensource.org/licenses/mit-license.php * https://www.gnu.org/licenses/gpl.html */ ;(function ($, w) { "use strict"; var methods = (function () { // private properties and methods go here var c = { bcClass: 'sf-breadcrumb', menuClass: 'sf-js-enabled', anchorClass: 'sf-with-ul', menuArrowClass: 'sf-arrows' }, ios = (function () { var ios = /^(?![\w\W]*Windows Phone)[\w\W]*(iPhone|iPad|iPod)/i.test(navigator.userAgent); if (ios) { // tap anywhere on iOS to unfocus a submenu $('html').css('cursor', 'pointer').on('click', $.noop); } return ios; })(), wp7 = (function () { var style = document.documentElement.style; return ('behavior' in style && 'fill' in style && /iemobile/i.test(navigator.userAgent)); })(), unprefixedPointerEvents = (function () { return (!!w.PointerEvent); })(), toggleMenuClasses = function ($menu, o) { var classes = c.menuClass; if (o.cssArrows) { classes += ' ' + c.menuArrowClass; } $menu.toggleClass(classes); }, setPathToCurrent = function ($menu, o) { return $menu.find('li.' + o.pathClass).slice(0, o.pathLevels) .addClass(o.hoverClass + ' ' + c.bcClass) .filter(function () { return ($(this).children(o.popUpSelector).hide().show().length); }).removeClass(o.pathClass); }, toggleAnchorClass = function ($li) { $li.children('a').toggleClass(c.anchorClass); }, toggleTouchAction = function ($menu) { var msTouchAction = $menu.css('ms-touch-action'); var touchAction = $menu.css('touch-action'); touchAction = touchAction || msTouchAction; touchAction = (touchAction === 'pan-y') ? 'auto' : 'pan-y'; $menu.css({ 'ms-touch-action': touchAction, 'touch-action': touchAction }); }, applyHandlers = function ($menu, o) { var targets = 'li:has(' + o.popUpSelector + ')'; if ($.fn.hoverIntent && !o.disableHI) { $menu.hoverIntent(over, out, targets); } else { $menu .on('mouseenter.superfish', targets, over) .on('mouseleave.superfish', targets, out); } var touchevent = 'MSPointerDown.superfish'; if (unprefixedPointerEvents) { touchevent = 'pointerdown.superfish'; } if (!ios) { touchevent += ' touchend.superfish'; } if (wp7) { touchevent += ' mousedown.superfish'; } $menu .on('focusin.superfish', 'li', over) .on('focusout.superfish', 'li', out) .on(touchevent, 'a', o, touchHandler); }, touchHandler = function (e) { var $this = $(this), o = getOptions($this), $ul = $this.siblings(e.data.popUpSelector); if (o.onHandleTouch.call($ul) === false) { return this; } if ($ul.length > 0 && $ul.is(':hidden')) { $this.one('click.superfish', false); if (e.type === 'MSPointerDown' || e.type === 'pointerdown') { $this.trigger('focus'); } else { $.proxy(over, $this.parent('li'))(); } } }, over = function () { var $this = $(this), o = getOptions($this); clearTimeout(o.sfTimer); $this.siblings().superfish('hide').end().superfish('show'); }, out = function () { var $this = $(this), o = getOptions($this); if (ios) { $.proxy(close, $this, o)(); } else { clearTimeout(o.sfTimer); o.sfTimer = setTimeout($.proxy(close, $this, o), o.delay); } }, close = function (o) { o.retainPath = ($.inArray(this[0], o.$path) > -1); this.superfish('hide'); if (!this.parents('.' + o.hoverClass).length) { o.onIdle.call(getMenu(this)); if (o.$path.length) { $.proxy(over, o.$path)(); } } }, getMenu = function ($el) { return $el.closest('.' + c.menuClass); }, getOptions = function ($el) { return getMenu($el).data('sf-options'); }; return { // public methods hide: function (instant) { if (this.length) { var $this = this, o = getOptions($this); if (!o) { return this; } var not = (o.retainPath === true) ? o.$path : '', $ul = $this.find('li.' + o.hoverClass).add(this).not(not).removeClass(o.hoverClass).children(o.popUpSelector), speed = o.speedOut; if (instant) { $ul.show(); speed = 0; } o.retainPath = false; if (o.onBeforeHide.call($ul) === false) { return this; } $ul.stop(true, true).animate(o.animationOut, speed, function () { var $this = $(this); o.onHide.call($this); }); } return this; }, show: function () { var o = getOptions(this); if (!o) { return this; } var $this = this.addClass(o.hoverClass), $ul = $this.children(o.popUpSelector); if (o.onBeforeShow.call($ul) === false) { return this; } $ul.stop(true, true).animate(o.animation, o.speed, function () { o.onShow.call($ul); }); return this; }, destroy: function () { return this.each(function () { var $this = $(this), o = $this.data('sf-options'), $hasPopUp; if (!o) { return false; } $hasPopUp = $this.find(o.popUpSelector).parent('li'); clearTimeout(o.sfTimer); toggleMenuClasses($this, o); toggleAnchorClass($hasPopUp); toggleTouchAction($this); // remove event handlers $this.off('.superfish').off('.hoverIntent'); // clear animation's inline display style $hasPopUp.children(o.popUpSelector).attr('style', function (i, style) { return style.replace(/display[^;]+;?/g, ''); }); // reset 'current' path classes o.$path.removeClass(o.hoverClass + ' ' + c.bcClass).addClass(o.pathClass); $this.find('.' + o.hoverClass).removeClass(o.hoverClass); o.onDestroy.call($this); $this.removeData('sf-options'); }); }, init: function (op) { return this.each(function () { var $this = $(this); if ($this.data('sf-options')) { return false; } var o = $.extend({}, $.fn.superfish.defaults, op), $hasPopUp = $this.find(o.popUpSelector).parent('li'); o.$path = setPathToCurrent($this, o); $this.data('sf-options', o); toggleMenuClasses($this, o); toggleAnchorClass($hasPopUp); toggleTouchAction($this); applyHandlers($this, o); $hasPopUp.not('.' + c.bcClass).superfish('hide', true); o.onInit.call(this); }); } }; })(); $.fn.superfish = function (method, args) { if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === 'object' || ! method) { return methods.init.apply(this, arguments); } else { return $.error('Method ' + method + ' does not exist on jQuery.fn.superfish'); } }; $.fn.superfish.defaults = { popUpSelector: 'ul,.sf-mega', // within menu context hoverClass: 'sfHover', pathClass: 'overrideThisToUse', pathLevels: 1, delay: 800, animation: {opacity: 'show'}, animationOut: {opacity: 'hide'}, speed: 'normal', speedOut: 'fast', cssArrows: true, disableHI: false, onInit: $.noop, onBeforeShow: $.noop, onShow: $.noop, onBeforeHide: $.noop, onHide: $.noop, onIdle: $.noop, onDestroy: $.noop, onHandleTouch: $.noop }; })(jQuery, window); /** * hoverIntent is similar to jQuery's built-in "hover" method except that * instead of firing the handlerIn function immediately, hoverIntent checks * to see if the user's mouse has slowed down (beneath the sensitivity * threshold) before firing the event. The handlerOut function is only * called after a matching handlerIn. * * hoverIntent r7 // 2013.03.11 // jQuery 1.9.1+ * http://cherne.net/brian/resources/jquery.hoverIntent.html * * You may use hoverIntent under the terms of the MIT license. Basically that * means you are free to use hoverIntent as long as this header is left intact. * Copyright 2007, 2013 Brian Cherne * * // basic usage ... just like .hover() * .hoverIntent( handlerIn, handlerOut ) * .hoverIntent( handlerInOut ) * * // basic usage ... with event delegation! * .hoverIntent( handlerIn, handlerOut, selector ) * .hoverIntent( handlerInOut, selector ) * * // using a basic configuration object * .hoverIntent( config ) * * @param handlerIn function OR configuration object * @param handlerOut function OR selector for delegation OR undefined * @param selector selector OR undefined * @author Brian Cherne **/ (function($) { $.fn.hoverIntent = function(handlerIn,handlerOut,selector) { // default configuration values var cfg = { interval: 100, sensitivity: 7, timeout: 0 }; if ( typeof handlerIn === "object" ) { cfg = $.extend(cfg, handlerIn ); } else if ($.isFunction(handlerOut)) { cfg = $.extend(cfg, { over: handlerIn, out: handlerOut, selector: selector } ); } else { cfg = $.extend(cfg, { over: handlerIn, out: handlerIn, selector: handlerOut } ); } // instantiate variables // cX, cY = current X and Y position of mouse, updated by mousemove event // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval var cX, cY, pX, pY; // A private function for getting mouse position var track = function(ev) { cX = ev.pageX; cY = ev.pageY; }; // A private function for comparing current and previous mouse position var compare = function(ev,ob) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); // compare mouse positions to see if they've crossed the threshold if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) { $(ob).off("mousemove.hoverIntent",track); // set hoverIntent state to true (so mouseOut can be called) ob.hoverIntent_s = 1; return cfg.over.apply(ob,[ev]); } else { // set previous coordinates for next time pX = cX; pY = cY; // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval ); } }; // A private function for delaying the mouseOut function var delay = function(ev,ob) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); ob.hoverIntent_s = 0; return cfg.out.apply(ob,[ev]); }; // A private function for handling mouse 'hovering' var handleHover = function(e) { // copy objects to be passed into t (required for event object to be passed in IE) var ev = jQuery.extend({},e); var ob = this; // cancel hoverIntent timer if it exists if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); } // if e.type == "mouseenter" if (e.type == "mouseenter") { // set "previous" X and Y position based on initial entry point pX = ev.pageX; pY = ev.pageY; // update "current" X and Y position based on mousemove $(ob).on("mousemove.hoverIntent",track); // start polling interval (self-calling timeout) to compare mouse coordinates over time if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );} // else e.type == "mouseleave" } else { // unbind expensive mousemove event $(ob).off("mousemove.hoverIntent",track); // if hoverIntent state is true, then call the mouseOut function after the specified delay if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );} } }; // listen for mouseenter and mouseleave return this.on({'mouseenter.hoverIntent':handleHover,'mouseleave.hoverIntent':handleHover}, cfg.selector); }; })(jQuery); /* Takes every category header available and adds a collapse option */ $(function() { if (smf_member_id > 0) $('div.boardindex_table div.cat_bar').each(function(index, el) { var catid = el.id.replace('category_', ''); new smc_Toggle({ bToggleEnabled: true, bCurrentlyCollapsed: $('#category_' + catid + '_upshrink').data('collapsed'), aHeader: $('#category_' + catid), aSwappableContainers: [ 'category_' + catid + '_boards' ], aSwapImages: [ { sId: 'category_' + catid + '_upshrink', msgExpanded: '', msgCollapsed: '' } ], oThemeOptions: { bUseThemeSettings: true, sOptionName: 'collapse_category_' + catid, sSessionVar: smf_session_var, sSessionId: smf_session_id } }); }); }); /* Mobile Pop */ $(function() { $( '.mobile_act' ).click(function() { $( '#mobile_action' ).show(); }); $( '.hide_popup' ).click(function() { $( '#mobile_action' ).hide(); }); $( '.mobile_mod' ).click(function() { $( '#mobile_moderation' ).show(); }); $( '.hide_popup' ).click(function() { $( '#mobile_moderation' ).hide(); }); $( '.mobile_user_menu' ).click(function() { $( '#mobile_user_menu' ).show(); }); $( '.hide_popup' ).click(function() { $( '#mobile_user_menu' ).hide(); }); });