Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Create the XenForo namespace
- * @package XenForo
- */
- var XenForo = {};
- // _XF_JS_UNCOMPRESSED_TEST_ - do not edit/remove
- if (jQuery === undefined) jQuery = $ = {};
- if ($.tools === undefined) console.error('jQuery Tools is not loaded.');
- function animateCSS(node, animationName, callback) {
- var isString = animationName instanceof String;
- if (isString) {
- animationName = ['animated', animationName];
- } else {
- animationName = ['animated'].concat(animationName);
- }
- for (var i = 0; i < animationName.length; i++) {
- node.classList.add(animationName[i]);
- }
- function handleAnimationEnd() {
- for (var i = 0; i < animationName.length; i++) {
- node.classList.remove(animationName[i]);
- }
- node.removeEventListener('animationend', handleAnimationEnd);
- if (typeof callback === 'function') callback()
- }
- node.addEventListener('animationend', handleAnimationEnd);
- }
- var isScrolledIntoView = function (elem) {
- var docViewTop = $(window).scrollTop() + $('#header').height();
- var docViewBottom = docViewTop + $(window).height();
- var elemTop = $(elem).offset().top;
- var elemBottom = elemTop + $(elem).height();
- return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
- };
- var supports_html5_storage = function () {
- try {
- return 'localStorage' in window && window['localStorage'] !== null;
- } catch (e) {
- return false;
- }
- };
- function isElementInViewport(el) {
- var rect = el[0].getBoundingClientRect();
- return (
- rect.top >= 0 &&
- rect.left >= 0 &&
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)
- );
- }
- function scrollParentToChild(parent, child) {
- var parentRect = parent.getBoundingClientRect();
- var parentViewableArea = { height: parent.clientHeight, width: parent.clientWidth };
- var childRect = child.getBoundingClientRect();
- var isViewable = (childRect.top >= parentRect.top) && (childRect.bottom <= parentRect.top + parentViewableArea.height);
- if (!isViewable) {
- const scrollTop = childRect.top - parentRect.top;
- parent.scrollTop += scrollTop;
- }
- }
- /**
- * Deal with Firebug not being present
- */
- !function (w) {
- var fn, i = 0;
- if (!w.console) w.console = {};
- if (w.console.log && !w.console.debug) w.console.debug = w.console.log;
- fn = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'getFirebugElement', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'notifyFirebug', 'profile', 'profileEnd', 'time', 'timeEnd', 'trace', 'warn'];
- for (i = 0; i < fn.length; ++i) if (!w.console[fn[i]]) w.console[fn[i]] = function () {
- };
- }(window);
- /** @param {jQuery} $ jQuery Object */
- !function ($, window, document, _undefined) {
- var isTouchBrowser = (function () {
- var _isTouchBrowserVal;
- try {
- _isTouchBrowserVal = 'ontouchstart' in document.documentElement;
- //_isTouchBrowserVal = !!('ontouchstart' in window || navigator.maxTouchPoints || navigator.msMaxTouchPoints);
- } catch (e) {
- _isTouchBrowserVal = !!(navigator.userAgent.indexOf('webOS') != -1);
- }
- return function () {
- return _isTouchBrowserVal;
- };
- })();
- var classes = ['hasJs'];
- classes.push(isTouchBrowser() ? 'Touch' : 'NoTouch');
- var div = document.createElement('div');
- classes.push(('draggable' in div) || ('ondragstart' in div && 'ondrop' in div) ? 'HasDragDrop' : 'NoDragDrop');
- // not especially nice but...
- if (navigator.userAgent.search(/\((iPhone|iPad|iPod);/) != -1) {
- classes.push('iOS');
- }
- var $html = $('html');
- $html.addClass(classes.join(' ')).removeClass('NoJs');
- /**
- * Fix IE abbr handling
- */
- document.createElement('abbr');
- /**
- * Detect mobile webkit
- */
- if (/webkit.*mobile/i.test(navigator.userAgent)) {
- XenForo._isWebkitMobile = true;
- }
- // preserve original jQuery Tools .overlay()
- jQuery.fn._jQueryToolsOverlay = jQuery.fn.overlay;
- /**
- * Extends jQuery core
- */
- jQuery.extend(true,
- {
- /**
- * Sets the context of 'this' within a called function.
- * Takes identical parameters to $.proxy, but does not
- * enforce the one-elment-one-method merging that $.proxy
- * does, allowing multiple objects of the same type to
- * bind to a single element's events (for example).
- *
- * @param function|object Function to be called | Context for 'this', method is a property of fn
- * @param function|string Context for 'this' | Name of method within fn to be called
- *
- * @return function
- */
- context: function (fn, context) {
- if (typeof context == 'string') {
- var _context = fn;
- fn = fn[context];
- context = _context;
- }
- return fn.bind(context)
- },
- /**
- * Sets a cookie.
- *
- * @param string cookie name (escaped)
- * @param mixed cookie value
- * @param string cookie expiry date
- *
- * @return mixed cookie value
- */
- setCookie: function (name, value, expires) {
- console.log('Set cookie %s = %s', name, value);
- document.cookie = XenForo._cookieConfig.prefix + name + '=' + encodeURIComponent(value)
- + (expires === undefined ? '' : ';expires=' + expires.toUTCString())
- + (XenForo._cookieConfig.path ? ';path=' + XenForo._cookieConfig.path : '')
- + (XenForo._cookieConfig.domain ? ';domain=' + XenForo._cookieConfig.domain : '');
- return value;
- },
- /**
- * Fetches the value of a named cookie.
- *
- * @param string Cookie name (escaped)
- *
- * @return string Cookie value
- */
- getCookie: function (name) {
- var expr, cookie;
- expr = new RegExp('(^| )' + XenForo._cookieConfig.prefix + name + '=([^;]+)(;|$)');
- cookie = expr.exec(document.cookie);
- if (cookie) {
- return decodeURIComponent(cookie[2]);
- } else {
- return null;
- }
- },
- /**
- * Deletes a cookie.
- *
- * @param string Cookie name (escaped)
- *
- * @return null
- */
- deleteCookie: function (name) {
- console.info('Delete cookie %s', name);
- document.cookie = XenForo._cookieConfig.prefix + name + '='
- + (XenForo._cookieConfig.path ? '; path=' + XenForo._cookieConfig.path : '')
- + (XenForo._cookieConfig.domain ? '; domain=' + XenForo._cookieConfig.domain : '')
- + '; expires=Thu, 01-Jan-70 00:00:01 GMT';
- return null;
- }
- });
- /**
- * Extends jQuery functions
- */
- jQuery.fn.extend(
- {
- /**
- * Wrapper for XenForo.activate, for 'this' element
- *
- * @return jQuery
- */
- xfActivate: function () {
- return XenForo.activate(this);
- },
- /**
- * Retuns .data(key) for this element, or the default value if there is no data
- *
- * @param string key
- * @param mixed defaultValue
- *
- * @return mixed
- */
- dataOrDefault: function (key, defaultValue) {
- var value = this.data(key);
- if (value === undefined) {
- return defaultValue;
- }
- return value;
- },
- /**
- * Like .val() but also trims trailing whitespace
- */
- strval: function () {
- return String(this.val()).replace(/\s+$/g, '');
- },
- /**
- * Get the 'name' attribute of an element, or if it exists, the value of 'data-fieldName'
- *
- * @return string
- */
- fieldName: function () {
- return this.data('fieldname') || this.attr('name');
- },
- /**
- * Get the value that would be submitted with 'this' element's name on form submit
- *
- * @return string
- */
- fieldValue: function () {
- switch (this.attr('type')) {
- case 'checkbox': {
- return $('input:checkbox[name="' + this.fieldName() + '"]:checked', this.context.form).val();
- }
- case 'radio': {
- return $('input:radio[name="' + this.fieldName() + '"]:checked', this.context.form).val();
- }
- default: {
- return this.val();
- }
- }
- },
- _jqSerialize: $.fn.serialize,
- /**
- * Overridden jQuery serialize method to ensure that RTE areas are serialized properly.
- */
- serialize: function () {
- $('textarea.BbCodeWysiwygEditor').each(function () {
- var data = $(this).data('XenForo.BbCodeWysiwygEditor');
- if (data) {
- data.syncEditor();
- }
- });
- return this._jqSerialize();
- },
- _jqSerializeArray: $.fn.serializeArray,
- /**
- * Overridden jQuery serializeArray method to ensure that RTE areas are serialized properly.
- */
- serializeArray: function () {
- $('textarea.BbCodeWysiwygEditor').each(function () {
- var data = $(this).data('XenForo.BbCodeWysiwygEditor');
- if (data) {
- data.syncEditor();
- }
- });
- return this._jqSerializeArray();
- },
- /**
- * Returns the position and size of an element, including hidden elements.
- *
- * If the element is hidden, it will very quickly un-hides a display:none item,
- * gets its offset and size, restore the element to its hidden state and returns values.
- *
- * @param string inner/outer/{none} Defines the jQuery size function to use
- * @param string offset/position/{none} Defines the jQuery position function to use (default: offset)
- *
- * @return object Offset { left: float, top: float }
- */
- coords: function (sizeFn, offsetFn) {
- var coords,
- visibility,
- display,
- widthFn,
- heightFn,
- hidden = this.is(':hidden');
- if (hidden) {
- visibility = this.css('visibility'),
- display = this.css('display');
- this.css(
- {
- visibility: 'hidden',
- display: 'block'
- });
- }
- switch (sizeFn) {
- case 'inner': {
- widthFn = 'innerWidth';
- heightFn = 'innerHeight';
- break;
- }
- case 'outer': {
- widthFn = 'outerWidth';
- heightFn = 'outerHeight';
- break;
- }
- default: {
- widthFn = 'width';
- heightFn = 'height';
- }
- }
- switch (offsetFn) {
- case 'position': {
- offsetFn = 'position';
- break;
- }
- default: {
- offsetFn = 'offset';
- break;
- }
- }
- coords = this[offsetFn]();
- coords.width = this[widthFn]();
- coords.height = this[heightFn]();
- if (hidden) {
- this.css(
- {
- display: display,
- visibility: visibility
- });
- }
- return coords;
- },
- /**
- * Sets a unique id for an element, if one is not already present
- */
- uniqueId: function () {
- if (!this.attr('id')) {
- this.attr('id', 'XenForoUniq' + XenForo._uniqueIdCounter++);
- }
- return this;
- },
- /**
- * Wrapper functions for commonly-used animation effects, so we can customize their behaviour as required
- */
- xfFadeIn: function (speed, callback) {
- return this.fadeIn(speed, function () {
- $(this).ieOpacityFix(callback);
- });
- },
- xfFadeOut: function (speed, callback) {
- return this.fadeOut(speed, callback);
- },
- xfShow: function (speed, callback) {
- return this.show(speed, function () {
- $(this).ieOpacityFix(callback);
- });
- },
- xfHide: function (speed, callback) {
- return this.hide(speed, callback);
- },
- xfSlideDown: function (speed, callback) {
- return this.slideDown(speed, function () {
- $(this).ieOpacityFix(callback);
- });
- },
- xfSlideUp: function (speed, callback) {
- return this.slideUp(speed, callback);
- },
- /**
- * Animates an element opening a space for itself, then fading into that space
- *
- * @param integer|string Speed of fade-in
- * @param function Callback function on completion
- *
- * @return jQuery
- */
- xfFadeDown: function (fadeSpeed, callback, finish) {
- this.filter(':hidden').xfHide().css('opacity', 0);
- fadeSpeed = fadeSpeed || XenForo.speed.normal;
- if (finish) {
- $(this).clearQueue().finish();
- }
- return this
- .xfSlideDown(XenForo.speed.fast)
- .animate({ opacity: 1 }, fadeSpeed, function () {
- $(this).ieOpacityFix(callback);
- });
- },
- /**
- * Animates an element fading out then closing the gap left behind
- *
- * @param integer|string Speed of fade-out - if this is zero, there will be no animation at all
- * @param function Callback function on completion
- * @param integer|string Slide speed - ignored if fadeSpeed is zero
- * @param string Easing method
- *
- * @return jQuery
- */
- xfFadeUp: function (fadeSpeed, callback, slideSpeed, easingMethod, finish) {
- fadeSpeed = ((typeof fadeSpeed == 'undefined' || fadeSpeed === null) ? XenForo.speed.normal : fadeSpeed);
- slideSpeed = ((typeof slideSpeed == 'undefined' || slideSpeed === null) ? fadeSpeed : slideSpeed);
- if (finish) {
- $(this).clearQueue().finish();
- }
- return this
- .slideUp({
- duration: Math.max(fadeSpeed, slideSpeed),
- easing: easingMethod || 'swing',
- complete: callback,
- queue: false
- })
- .animate({ opacity: 0, queue: false }, fadeSpeed);
- },
- /**
- * Inserts and activates content into the DOM, using xfFadeDown to animate the insertion
- *
- * @param string jQuery method with which to insert the content
- * @param string Selector for the previous parameter
- * @param string jQuery method with which to animate the showing of the content
- * @param string|integer Speed at which to run the animation
- * @param function Callback for when the animation is complete
- *
- * @return jQuery
- */
- xfInsert: function (insertMethod, insertReference, animateMethod, animateSpeed, callback) {
- if (insertMethod == 'replaceAll') {
- $(insertReference).xfFadeUp(animateSpeed);
- }
- this
- .addClass('__XenForoActivator')
- .css('display', 'none')
- [insertMethod || 'appendTo'](insertReference)
- .xfActivate()
- [animateMethod || 'xfFadeDown'](animateSpeed, callback);
- return this;
- },
- /**
- * Removes an element from the DOM, animating its removal with xfFadeUp
- * All parameters are optional.
- *
- * @param string animation method
- * @param function callback function
- * @param integer Sliding speed
- * @param string Easing method
- *
- * @return jQuery
- */
- xfRemove: function (animateMethod, callback, slideSpeed, easingMethod) {
- return this[animateMethod || 'xfFadeUp'](XenForo.speed.normal, function () {
- $(this).empty().remove();
- if ($.isFunction(callback)) {
- callback();
- }
- }, slideSpeed, easingMethod);
- },
- /**
- * Prepares an element for xfSlideIn() / xfSlideOut()
- *
- * @param boolean If true, return the height of the wrapper
- *
- * @return jQuery|integer
- */
- _xfSlideWrapper: function (getHeight) {
- if (!this.data('slidewrapper')) {
- this.data('slidewrapper', this.wrap('<div class="_swOuter"><div class="_swInner" /></div>')
- .closest('div._swOuter').css('overflow', 'hidden'));
- }
- if (getHeight) {
- try {
- return this.data('slidewrapper').height();
- } catch (e) {
- // so IE11 seems to be randomly throwing an exception in jQuery here, so catch it
- return 0;
- }
- }
- return this.data('slidewrapper');
- },
- /**
- * Slides content in (down), with content glued to lower edge, drawer-like
- *
- * @param duration
- * @param easing
- * @param callback
- *
- * @return jQuery
- */
- xfSlideIn: function (duration, easing, callback) {
- var $wrap = this._xfSlideWrapper().css('height', 'auto'),
- height = 0;
- $wrap.find('div._swInner').css('margin', 'auto');
- height = this.show(0).outerHeight();
- $wrap
- .css('height', 0)
- .animate({ height: height }, duration, easing, function () {
- $wrap.css('height', '');
- })
- .find('div._swInner')
- .css('marginTop', height * -1)
- .animate({ marginTop: 0 }, duration, easing, callback);
- return this;
- },
- /**
- * Slides content out (up), reversing xfSlideIn()
- *
- * @param duration
- * @param easing
- * @param callback
- *
- * @return jQuery
- */
- xfSlideOut: function (duration, easing, callback) {
- var height = this.outerHeight();
- this._xfSlideWrapper()
- .animate({ height: 0 }, duration, easing)
- .find('div._swInner')
- .animate({ marginTop: height * -1 }, duration, easing, callback);
- return this;
- },
- /**
- * Workaround for IE's font-antialiasing bug when dealing with opacity
- *
- * @param function Callback
- */
- ieOpacityFix: function (callback) {
- //ClearType Fix
- if (!$.support.opacity) {
- this.css('filter', '');
- this.attr('style', this.attr('style').replace(/filter:\s*;/i, ''));
- }
- if ($.isFunction(callback)) {
- callback.apply(this);
- }
- return this;
- },
- /**
- * Wraps around jQuery Tools .overlay().
- *
- * Prepares overlay options before firing overlay() for best possible experience.
- * For example, removes fancy (slow) stuff from options for touch browsers.
- *
- * @param options
- *
- * @returns jQuery
- */
- overlay: function (options) {
- if (XenForo.isTouchBrowser()) {
- return this._jQueryToolsOverlay($.extend(true, options,
- {
- //mask: false,
- speed: 0,
- loadSpeed: 0
- }));
- } else {
- return this._jQueryToolsOverlay(options);
- }
- }
- });
- /* jQuery Tools Extensions */
- /**
- * Effect method for jQuery.tools overlay.
- * Slides down a container, then fades up the content.
- * Closes by reversing the animation.
- */
- $.tools.overlay.addEffect('zoomIn',
- function (position, callback) {
- var $overlay = this.getOverlay();
- $overlay.find('.content').css('opacity', 0);
- if (this.getConf().fixed) {
- position.position = 'fixed';
- } else {
- position.position = 'absolute';
- position.top += $(window).scrollTop();
- position.left += $(window).scrollLeft();
- }
- $overlay.removeClass('animated zoomOut faster').css(position);
- $overlay.show(0, callback);
- animateCSS($overlay.get(0), ['zoomIn', 'faster']);
- },
- function (callback) {
- var $overlay = this.getOverlay();
- $overlay.removeClass('animated zoomIn faster');
- animateCSS($overlay.get(0), ['zoomOut', 'faster'], function () {
- callback();
- $overlay.hide(0, callback);
- });
- },
- );
- $.tools.overlay.addEffect('slideDownContentFade',
- function (position, callback) {
- var $overlay = this.getOverlay(),
- conf = this.getConf();
- $overlay.find('.content').css('opacity', 0);
- if (this.getConf().fixed) {
- position.position = 'fixed';
- } else {
- position.position = 'absolute';
- position.top += $(window).scrollTop();
- position.left += $(window).scrollLeft();
- }
- $overlay.css(position).xfSlideDown(XenForo.speed.fast, function () {
- $overlay.find('.content').animate({ opacity: 1 }, conf.speed, function () {
- $(this).ieOpacityFix(callback);
- });
- });
- },
- function (callback) {
- var $overlay = this.getOverlay();
- $overlay.find('.content').animate({ opacity: 0 }, this.getConf().speed, function () {
- $overlay.xfSlideUp(XenForo.speed.fast, callback);
- });
- }
- );
- $.tools.overlay.addEffect('slideDown',
- function (position, callback) {
- if (this.getConf().fixed) {
- position.position = 'fixed';
- } else {
- position.position = 'absolute';
- position.top += $(window).scrollTop();
- position.left += $(window).scrollLeft();
- }
- this.getOverlay()
- .css(position)
- .xfSlideDown(this.getConf().speed, callback);
- },
- function (callback) {
- this.getOverlay().hide(0, callback);
- }
- );
- // *********************************************************************
- $.extend(XenForo,
- {
- /**
- * Cache for overlays
- *
- * @var object
- */
- _OverlayCache: {},
- /**
- * Defines whether or not an AJAX request is known to be in progress
- *
- * @var boolean
- */
- _AjaxProgress: false,
- /**
- * Defines a variable that can be overridden to force/control the base HREF
- * used to canonicalize AJAX requests
- *
- * @var string
- */
- ajaxBaseHref: '',
- /**
- * Counter for unique ID generation
- *
- * @var integer
- */
- _uniqueIdCounter: 0,
- /**
- * Configuration for overlays, should be redefined in the PAGE_CONTAINER template HTML
- *
- * @var object
- */
- _overlayConfig: {},
- /**
- * Contains the URLs of all externally loaded resources from scriptLoader
- *
- * @var object
- */
- _loadedScripts: {},
- /**
- * Configuration for cookies
- *
- * @var object
- */
- _cookieConfig: { path: '/', domain: '', 'prefix': 'xf_' },
- /**
- * Flag showing whether or not the browser window has focus. On load, assume true.
- *
- * @var boolean
- */
- _hasFocus: true,
- /**
- * @var object List of server-related time info (now, today, todayDow)
- */
- serverTimeInfo: {},
- /**
- * @var object Information about the XenForo visitor. Usually contains user_id.
- */
- visitor: {},
- /**
- * @var integer Time the page was loaded.
- */
- _pageLoadTime: (new Date()).getTime() / 1000,
- /**
- * JS version key, to force refreshes when needed
- *
- * @var string
- */
- _jsVersion: '',
- /**
- * If true, disables reverse tabnabbing protection
- *
- * @var bool
- */
- _noRtnProtect: false,
- /**
- * CSRF Token
- *
- * @var string
- */
- _csrfToken: '',
- /**
- * URL to CSRF token refresh.
- *
- * @var string
- */
- _csrfRefreshUrl: '',
- _noSocialLogin: false,
- /**
- * Speeds for animation
- *
- * @var object
- */
- speed:
- {
- xxfast: 50,
- xfast: 100,
- fast: 200,
- normal: 400,
- slow: 600
- },
- /**
- * Multiplier for animation speeds
- *
- * @var float
- */
- _animationSpeedMultiplier: 1,
- /**
- * Enable overlays or use regular pages
- *
- * @var boolean
- */
- _enableOverlays: true,
- /**
- * Enables AJAX submission via AutoValidator. Doesn't change things other than
- * that. Useful to disable for debugging.
- *
- * @var boolean
- */
- _enableAjaxSubmit: true,
- /**
- * Determines whether the lightbox shows all images from the current page,
- * or just from an individual message
- *
- * @var boolean
- */
- _lightBoxUniversal: false,
- /**
- * @var object Phrases
- */
- phrases: {},
- /**
- * Binds all registered functions to elements within the DOM
- */
- init: function () {
- var dStart = new Date(),
- xfFocus = function () {
- XenForo._hasFocus = true;
- $(document).triggerHandler('XenForoWindowFocus');
- },
- xfBlur = function () {
- XenForo._hasFocus = false;
- $(document).triggerHandler('XenForoWindowBlur');
- },
- $html = $('html');
- if ($.browser.msie) {
- $(document).bind(
- {
- focusin: xfFocus,
- focusout: xfBlur
- });
- } else {
- $(window).bind(
- {
- focus: xfFocus,
- blur: xfBlur
- });
- }
- $(window).on('resize', function () {
- XenForo.checkQuoteSizing($(document));
- });
- // Set the animation speed based around the style property speed multiplier
- XenForo.setAnimationSpeed(XenForo._animationSpeedMultiplier);
- // Periodical timestamp refresh
- XenForo._TimestampRefresh = new XenForo.TimestampRefresh();
- // Find any ignored content that has not been picked up by PHP
- XenForo.prepareIgnoredContent();
- // init ajax progress indicators
- XenForo.AjaxProgress();
- // Activate all registered controls
- XenForo.activate(document);
- $(document).on('click', '.bbCodeQuote .quoteContainer .quoteExpand', function (e) {
- $(this).closest('blockquote.quote').css('max-height', 'unset')
- $(this).closest('.quoteContainer').toggleClass('expanded');
- });
- XenForo.watchProxyLinks();
- if (!XenForo._noRtnProtect) {
- XenForo.watchExternalLinks();
- }
- // make the breadcrumb and navigation responsive
- if (!$html.hasClass('NoResponsive')) {
- XenForo.updateVisibleBreadcrumbs();
- XenForo.updateVisibleNavigationTabs();
- XenForo.updateVisibleNavigationLinks();
- var resizeTimer, htmlWidth = $html.width();
- $(window).on('resize orientationchange load', function (e) {
- if (resizeTimer) {
- return;
- }
- if (e.type != 'load' && $html.width() == htmlWidth) {
- return;
- }
- htmlWidth = $html.width();
- resizeTimer = setTimeout(function () {
- resizeTimer = 0;
- XenForo.updateVisibleBreadcrumbs();
- XenForo.updateVisibleNavigationTabs();
- XenForo.updateVisibleNavigationLinks();
- }, 20);
- });
- $(document).on('click', '.breadcrumb .placeholder', function () {
- $(this).closest('.breadcrumb').addClass('showAll');
- XenForo.updateVisibleBreadcrumbs();
- });
- }
- // Periodical CSRF token refresh
- XenForo._CsrfRefresh = new XenForo.CsrfRefresh();
- // Autofocus for non-supporting browsers
- if (!('autofocus' in document.createElement('input'))) {
- //TODO: work out a way to prevent focusing if something else already has focus http://www.w3.org/TR/html5/forms.html#attr-fe-autofocus
- $('input[autofocus], textarea[autofocus], select[autofocus]').first().focus();
- }
- // init Tweet buttons
- XenForo.tweetButtonInit();
- console.info('XenForo.init() %dms. jQuery %s/%s', new Date() - dStart, $().jquery, $.tools.version);
- if ($('#ManualDeferredTrigger').length) {
- setTimeout(XenForo.manualDeferredHandler, 100);
- }
- if ($('html.RunDeferred').length) {
- setTimeout(XenForo.runAutoDeferred, 100);
- }
- },
- runAutoDeferred: function () {
- XenForo.ajax('deferred.php', {}, function (ajaxData) {
- if (ajaxData && ajaxData.moreDeferred) {
- setTimeout(XenForo.runAutoDeferred, 100);
- }
- }, { error: false, global: false });
- },
- prepareIgnoredContent: function () {
- var $displayLink = $('a.DisplayIgnoredContent'),
- namesObj = {},
- namesArr = [];
- if ($displayLink.length) {
- $('.ignored').each(function () {
- var name = $(this).data('author');
- if (name) {
- namesObj[name] = true;
- }
- });
- $.each(namesObj, function (name) {
- namesArr.push(name);
- });
- if (namesArr.length) {
- $displayLink.attr('title', XenForo.phrases['show_hidden_content_by_x'].replace(/\{names\}/, namesArr.join(', ')));
- $displayLink.parent().show();
- }
- }
- },
- watchProxyLinks: function () {
- var proxyLinkClick = function (e) {
- var $this = $(this),
- proxyHref = $this.data('proxy-href'),
- lastEvent = $this.data('proxy-handler-last');
- if (!proxyHref) {
- return;
- }
- // we may have a direct click event and a bubbled event. Ensure they don't both fire.
- if (lastEvent && lastEvent == e.timeStamp) {
- return;
- }
- $this.data('proxy-handler-last', e.timeStamp);
- XenForo.ajax(proxyHref, {}, function (ajaxData) {
- }, { error: false, global: false });
- };
- $(document)
- .on('click', 'a.ProxyLink', proxyLinkClick)
- .on('focusin', 'a.ProxyLink', function (e) {
- // This approach is taken because middle click events do not bubble. This is a way of
- // getting the equivalent of event bubbling on middle clicks in Chrome.
- var $this = $(this);
- if ($this.data('proxy-handler')) {
- return;
- }
- $this.data('proxy-handler', true)
- .click(proxyLinkClick);
- });
- },
- watchExternalLinks: function () {
- var externalLinkClick = function (e) {
- if (e.isDefaultPrevented()) {
- return;
- }
- var $this = $(this),
- href = $this.attr('href'),
- lastEvent = $this.data('blank-handler-last');
- if (!href) {
- return;
- }
- const urlRegExp = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/;
- if (!urlRegExp.test(href)) {
- return;
- }
- href = XenForo.canonicalizeUrl(href);
- var regex = new RegExp('^[a-z]+://' + location.host + '(/|$|:)', 'i');
- if (regex.test(href) && !$this.hasClass('ProxyLink')) {
- // if the link is local, then don't do the special processing... unless it's a proxy link
- // so it's likely to be external after the redirect
- return;
- }
- // we may have a direct click event and a bubbled event. Ensure they don't both fire.
- if (lastEvent && lastEvent == e.timeStamp) {
- return;
- }
- $this.data('blank-handler-last', e.timeStamp);
- var ua = navigator.userAgent,
- isOldIE = ua.indexOf('MSIE') !== -1,
- isSafari = ua.indexOf('Safari') !== -1 && ua.indexOf('Chrome') == -1,
- isGecko = ua.indexOf('Gecko/') !== -1;
- if (e.shiftKey && isGecko) {
- // Firefox doesn't trigger when holding shift. If the code below runs, it will force
- // opening in a new tab instead of a new window, so stop. Note that Chrome still triggers here,
- // but it does open in a new window anyway so we run the normal code.
- return;
- }
- if (isSafari && (e.shiftKey || e.altKey)) {
- // this adds to reading list or downloads instead of opening a new tab
- return;
- }
- if (isOldIE) {
- // IE has mitigations for this and this blocks referrers
- return;
- }
- // now run the opener clearing
- if (isSafari) {
- // Safari doesn't work with the other approach
- // Concept from: https://github.com/danielstjules/blankshield
- var $iframe, iframeDoc, $script;
- $iframe = $('<iframe style="display: none" />').appendTo(document.body);
- iframeDoc = $iframe[0].contentDocument || $iframe[0].contentWindow.document;
- iframeDoc.__href = href; // set this so we don't need to do an eval-type thing
- $script = $('<script />', iframeDoc);
- $script[0].text = 'window.opener=null;' +
- 'window.parent=null;window.top=null;window.frameElement=null;' +
- 'window.open(document.__href).opener = null;';
- iframeDoc.body.appendChild($script[0]);
- $iframe.remove();
- } else {
- // use this approach for the rest to maintain referrers when possible
- var w = window.open(href);
- try {
- // this can potentially fail, don't want to break
- w.opener = null;
- } catch (e) {
- }
- }
- e.preventDefault();
- };
- $(document)
- .on('click', 'a[target=_blank]:not(.fr-element a)', externalLinkClick)
- .on('focusin', 'a[target=_blank]:not(.fr-element a)', function (e) {
- // This approach is taken because middle click events do not bubble. This is a way of
- // getting the equivalent of event bubbling on middle clicks in Chrome.
- var $this = $(this);
- if ($this.data('blank-handler')) {
- return;
- }
- $this.data('blank-handler', true)
- .click(externalLinkClick);
- });
- },
- /**
- * Asynchronously load the specified JavaScript, with an optional callback on completion.
- *
- * @param string Script source
- * @param object Callback function
- * @param string innerHtml for the script tags
- */
- loadJs: function (src, callback, innerHTML) {
- try {
- var script = document.createElement('script');
- script.async = true;
- if (innerHTML) {
- try {
- script.innerHTML = innerHTML;
- } catch (e2) {
- }
- }
- var f = function () {
- if (callback) {
- callback();
- callback = null;
- }
- };
- script.onload = f;
- script.onreadystatechange = function () {
- if (script.readyState === 'loaded') {
- f();
- }
- };
- script.src = src;
- document.getElementsByTagName('head')[0].appendChild(script);
- } catch (e) {
- }
- },
- /**
- * Asynchronously load the Twitter button JavaScript.
- */
- tweetButtonInit: function () {
- if ($('a.twitter-share-button').length) {
- XenForo.loadJs('https://platform.twitter.com/widgets.js');
- }
- },
- /**
- * Asynchronously load the +1 button JavaScript.
- */
- plusoneButtonInit: function (el) {
- if ($(el).find('div.g-plusone, .GoogleLogin').length) {
- var locale = $('html').attr('lang');
- var callback = function () {
- if (!window.gapi) {
- return;
- }
- $(el).find('.GoogleLogin').each(function () {
- var $button = $(this),
- clientId = $button.data('client-id'),
- auth2;
- gapi.load('auth2', function () {
- auth2 = gapi.auth2.init({
- client_id: clientId,
- scope: 'profile email',
- response_type: 'permission',
- cookie_policy: 'single_host_origin'
- });
- $button.on('click', function () {
- auth2.grantOfflineAccess({
- scope: 'profile email'
- })
- .then(function (resp) {
- var code = resp.code;
- if (code) {
- window.location = XenForo.canonicalizeUrl(
- $button.data('redirect-url').replace('__CODE__', code)
- );
- }
- })
- .catch(function () {
- });
- });
- });
- });
- };
- if (window.___gcfg && window.gapi) {
- callback();
- } else {
- window.___gcfg = {
- lang: locale,
- isSignedOut: true // this is to stop the "welcome back" prompt as it doesn't fit with our flow
- };
- XenForo.loadJs('https://apis.google.com/js/api:client.js', callback);
- }
- }
- },
- /**
- * Prevents Google Chrome's AutoFill from turning inputs yellow.
- * Adapted from http://www.benjaminmiles.com/2010/11/22/fixing-google-chromes-yellow-autocomplete-styles-with-jquery/
- */
- chromeAutoFillFix: function ($root) {
- if ($.browser.webkit && navigator.userAgent.toLowerCase().indexOf('chrome') >= 0) {
- if (!$root) {
- $root = $(document);
- }
- // trap an error here - CloudFlare RocketLoader causes an error with this.
- var $inputs;
- try {
- $inputs = $root.find('input:-webkit-autofill');
- } catch (e) {
- $inputs = $([]);
- }
- if ($inputs.length) {
- $inputs.each(function (i) {
- var $this = $(this),
- val = $this.val();
- if (!val || !val.length) {
- return;
- }
- $this.after($this.clone(true).val(val)).remove();
- });
- }
- }
- },
- updateVisibleBreadcrumbs: function () {
- $('.breadcrumb').each(function () {
- var container = this,
- $container = $(container);
- $container.find('.placeholder').remove();
- var $crusts = $container.find('.crust');
- $crusts.removeClass('firstVisibleCrumb').show();
- var $homeCrumb = $crusts.filter('.homeCrumb');
- $container.css('height', '');
- var beforeHeight = container.offsetHeight;
- $container.css('height', 'auto');
- if (container.offsetHeight <= beforeHeight) {
- $container.css('height', '');
- return;
- }
- var $lastHidden = null,
- hideSkipSelector = '.selectedTabCrumb, :last-child';
- $crusts.each(function () {
- var $crust = $(this);
- if ($crust.is(hideSkipSelector)) {
- return true;
- }
- $crust.hide();
- $lastHidden = $crust;
- return (container.offsetHeight > beforeHeight);
- });
- if (!$lastHidden) {
- $container.css('height', '');
- return;
- }
- var $placeholder = $('<span class="crust placeholder"><a class="crumb" href="javascript:"><span>...</span></a><span class="arrow"><span>></span></span></span>');
- $lastHidden.after($placeholder);
- if (container.offsetHeight > beforeHeight) {
- var $prev = $lastHidden.prevAll('.crust:not(' + hideSkipSelector + ')').last();
- if ($prev.length) {
- $prev.hide();
- }
- }
- if (container.offsetHeight > beforeHeight) {
- var $next = $lastHidden.nextAll('.crust:not(.placeholder, ' + hideSkipSelector + ')').first();
- if ($next.length) {
- $next.hide();
- $next.after($placeholder);
- }
- }
- if ($homeCrumb.length && !$homeCrumb.is(':visible')) {
- $container.find('.crust:visible:first').addClass('firstVisibleCrumb');
- }
- if (container.offsetHeight <= beforeHeight) {
- // firefox doesn't seem to contain the breadcrumbs despite the overflow hidden
- $container.css('height', '');
- }
- });
- },
- updateVisibleNavigationTabs: function () {
- var $tabs = $('#navigation').find('.navTabs');
- if (!$tabs.length) {
- return;
- }
- var tabsCoords = $tabs.coords(),
- $publicTabs = $tabs.find('.publicTabs'),
- $publicInnerTabs = $publicTabs.find('> .navTab'),
- $visitorTabs = $tabs.find('.visitorTabs'),
- $visitorInnerTabs = $visitorTabs.find('> .navTab'),
- $visitorCounter = $('#VisitorExtraMenu_Counter'),
- maxPublicWidth,
- $hiddenTab = $publicInnerTabs.filter('.navigationHiddenTabs');
- $publicInnerTabs.show();
- $hiddenTab.hide();
- $visitorInnerTabs.show();
- $visitorCounter.addClass('ResponsiveOnly');
- if ($tabs.is('.showAll')) {
- return;
- }
- maxPublicWidth = $tabs.width() - $visitorTabs.width() - 1;
- var hidePublicTabs = function () {
- var shownSel = '.selected, .navigationHiddenTabs';
- var $hideable = $publicInnerTabs.filter(':not(' + shownSel + ')'),
- $hiddenList = $('<ul />'),
- hiddenCount = 0,
- overflowMenuShown = false;
- $.each($hideable.get().reverse(), function () {
- var $this = $(this);
- if (isOverflowing($publicTabs.coords(), true)) {
- $hiddenList.prepend(
- $('<li />').html($this.find('.navLink').clone())
- );
- $this.hide();
- hiddenCount++;
- } else {
- if (hiddenCount) {
- $hiddenTab.show();
- if (isOverflowing($publicTabs.coords(), true)) {
- $hiddenList.prepend(
- $('<li />').html($this.find('.navLink').clone())
- );
- $this.hide();
- hiddenCount++;
- }
- $('#NavigationHiddenMenu').html($hiddenList).xfActivate();
- overflowMenuShown = true;
- } else {
- $hiddenTab.hide();
- }
- return false;
- }
- });
- if (hiddenCount && !overflowMenuShown) {
- $hiddenTab.show();
- $('#NavigationHiddenMenu').html($hiddenList).xfActivate();
- }
- },
- hideVisitorTabs = function () {
- $visitorInnerTabs.hide();
- $visitorInnerTabs.filter('.account, .selected').show();
- $visitorCounter.removeClass('ResponsiveOnly');
- },
- isOverflowing = function (coords, checkMax) {
- if (
- coords.top >= tabsCoords.top + tabsCoords.height
- || coords.height >= tabsCoords.height * 2
- ) {
- return true;
- }
- if (checkMax && coords.width > maxPublicWidth) {
- return true;
- }
- return false;
- };
- if ($visitorTabs.length) {
- if (isOverflowing($visitorTabs.coords())) {
- hidePublicTabs();
- if (isOverflowing($visitorTabs.coords())) {
- hideVisitorTabs();
- }
- }
- } else if (isOverflowing($publicTabs.coords())) {
- hidePublicTabs();
- }
- },
- updateVisibleNavigationLinks: function () {
- var $linksList = $('#navigation').find('.navTab.selected .blockLinksList');
- if (!$linksList.length) {
- return;
- }
- var $links = $linksList.find('> li'),
- listOffset = $linksList.offset(),
- $hidden = $links.filter('.navigationHidden'),
- $firstHidden = false;
- $links.show();
- $hidden.hide();
- if ($linksList.is('.showAll')) {
- return;
- }
- var hiddenForMenu = [],
- $lastLink = $links.filter(':not(.navigationHidden)').last(),
- hideOffset = 0,
- hasHidden = false,
- lastCoords,
- $link;
- if (!$lastLink.length) {
- return;
- }
- do {
- lastCoords = $lastLink.coords();
- if (lastCoords.top > listOffset.top + lastCoords.height) {
- $link = $links.eq(hideOffset);
- $link.hide();
- hiddenForMenu.push($link);
- hideOffset++;
- if (!hasHidden) {
- hasHidden = true;
- if (!$hidden.length) {
- $hidden = $('<li class="navigationHidden Popup PopupControl PopupClosed"><a rel="Menu" class="NoPopupGadget">...</a><div class="Menu blockLinksList primaryContent" id="NavigationLinksHiddenMenu"></div></li>');
- $linksList.append($hidden);
- new XenForo.PopupMenu($hidden);
- } else {
- $hidden.show();
- }
- }
- } else {
- break;
- }
- }
- while (hideOffset < $links.length);
- if (hasHidden) {
- if (hideOffset < $links.length) {
- var coords = $hidden.coords();
- if (coords.top > listOffset.top + coords.height) {
- $link = $links.eq(hideOffset);
- $link.hide();
- hiddenForMenu.push($link);
- }
- }
- var $hiddenList = $('<ul />');
- $(hiddenForMenu).each(function () {
- $hiddenList.append(
- $('<li />').html($(this).find('a').clone())
- );
- });
- $('#NavigationLinksHiddenMenu').html($hiddenList).xfActivate();
- }
- },
- /**
- * Binds a function to elements to fire on a custom event
- *
- * @param string jQuery selector - to get the elements to be bound
- * @param function Function to fire
- * @param string Custom event name (if empty, assume 'XenForoActivateHtml')
- */
- register: function (selector, fn, event) {
- if (typeof fn == 'string') {
- var className = fn;
- fn = function (i) {
- XenForo.create(className, this);
- };
- }
- $(document).bind(event || 'XenForoActivateHtml', function (e) {
- $(e.element).find(selector).each(fn);
- });
- },
- /**
- * Creates a new object of class XenForo.{functionName} using
- * the specified element, unless one has already been created.
- *
- * @param string Function name (property of XenForo)
- * @param object HTML element
- *
- * @return object XenForo[functionName]($(element))
- */
- create: function (className, element) {
- var $element = $(element),
- xfObj = window,
- parts = className.split('.'), i;
- for (i = 0; i < parts.length; i++) {
- xfObj = xfObj[parts[i]];
- }
- if (typeof xfObj != 'function') {
- return console.error('%s is not a function.', className);
- }
- if (!$element.data(className)) {
- $element.data(className, new xfObj($element));
- }
- return $element.data(className);
- },
- /**
- * Fire the initialization events and activate functions for the specified element
- *
- * @param object Usually jQuery
- *
- * @return object
- */
- activate: function (element) {
- var $element = $(element);
- console.group('XenForo.activate(%o)', element);
- $element.trigger('XenForoActivate').removeClass('__XenForoActivator');
- $element.find('noscript').empty().remove();
- XenForo._TimestampRefresh.refresh(element, true);
- $(document)
- .trigger({ element: element, type: 'XenForoActivateHtml' })
- .trigger({ element: element, type: 'XenForoActivatePopups' })
- .trigger({ element: element, type: 'XenForoActivationComplete' });
- var $form = $element.find('form.AutoSubmit:first');
- if ($form.length) {
- $(document).trigger('PseudoAjaxStart');
- $form.submit();
- $form.find('input[type="submit"], input[type="reset"]').hide();
- }
- XenForo.checkQuoteSizing($element);
- XenForo.plusoneButtonInit(element);
- //XenForo.Facebook.start();
- console.groupEnd();
- return element;
- },
- checkQuoteSizing: function ($element) {
- $element.find('.bbCodeQuote .quoteContainer').each(function () {
- var self = this,
- delay = 0,
- checkHeight = function () {
- var $self = $(self),
- quote = $self.find('.quote')[0];
- if (!quote) {
- return;
- }
- if (quote.scrollHeight == 0 || quote.offsetHeight == 0) {
- if (delay < 2000) {
- setTimeout(checkHeight, delay);
- delay += 100;
- }
- return;
- }
- // +1 resolves a chrome rounding issue
- if (quote.scrollHeight > parseInt($(quote).css('max-height')) && quote.scrollHeight > quote.offsetHeight + 1) {
- $self.find('.quoteExpand').addClass('quoteCut');
- } else {
- $self.find('.quoteExpand').removeClass('quoteCut');
- }
- };
- checkHeight();
- $(this).find('img, iframe').one('load', checkHeight);
- $(this).on('elementResized', checkHeight);
- });
- },
- /**
- * Pushes an additional parameter onto the data to be submitted via AJAX
- *
- * @param array|string Data parameters - either from .serializeArray() or .serialize()
- * @param string Name of parameter
- * @param mixed Value of parameter
- *
- * @return array|string Data including new parameter
- */
- ajaxDataPush: function (data, name, value) {
- if (!data || typeof data == 'string') {
- // data is empty, or a url string - &name=value
- data = String(data);
- data += '&' + encodeURIComponent(name) + '=' + encodeURIComponent(value);
- } else if (data instanceof FormData) {
- // data is FormData
- data.append(name, value)
- } else if (data[0] !== undefined) {
- // data is a numerically-keyed array of name/value pairs
- data.push({ name: name, value: value });
- } else {
- // data is an object with a single set of name & value properties
- data[name] = value;
- }
- return data;
- },
- /**
- * Wraps around jQuery's own $.ajax function, with our own defaults provided.
- * Will submit via POST and expect JSON back by default.
- * Server errors will be handled using XenForo.handleServerError
- *
- * @param string URL to load
- * @param object Data to pass
- * @param function Success callback function
- * @param object Additional options to override or extend defaults
- *
- * @return XMLHttpRequest
- */
- ajax: function (url, data, success, options) {
- if (!url) {
- return console.error('No URL specified for XenForo.ajax()');
- }
- url = XenForo.canonicalizeUrl(url, XenForo.ajaxBaseHref);
- try {
- let url_element = new URL(url);
- if (url_element.pathname === "/index.php") {
- url_element.pathname = url_element.search.split("&")[0].slice(1)
- url_element.search = "?" + url_element.search.slice(url_element.search.split("&")[0].length + 1)
- }
- if (url_element.pathname.indexOf('&') !== -1) {
- let elements = url_element.pathname.split("&");
- url_element.pathname = elements[0]
- elements.shift();
- url_element.search = "?" + elements.join("&")
- }
- url = url_element.toString()
- } catch (e) {
- }
- data = XenForo.ajaxDataPush(data, '_xfRequestUri', window.location.pathname + window.location.search);
- data = XenForo.ajaxDataPush(data, '_xfNoRedirect', 1);
- if (XenForo._csrfToken) {
- data = XenForo.ajaxDataPush(data, '_xfToken', XenForo._csrfToken);
- }
- var successCallback = function (ajaxData, textStatus) {
- if (typeof ajaxData == 'object') {
- if (typeof ajaxData._visitor_conversationsUnread != 'undefined') {
- if ($('#AlertsMenu_Counter').length && ajaxData._visitor_alertsUnread != '0' && $('#AlertsMenu_Counter').text().trim() != ajaxData._visitor_alertsUnread) {
- const svg = $('#AlertsMenu_Counter')
- .closest('.counter-container')
- .find('svg')
- .attr('class', '') // через addClass на svg не хочет
- setTimeout(() => { svg.attr('class', 'animated tada') }, 1)
- }
- XenForo.balloonCounterUpdate($('#ConversationsMenu_Counter'), ajaxData._visitor_conversationsUnread);
- XenForo.balloonCounterUpdate($('#AlertsMenu_Counter'), ajaxData._visitor_alertsUnread);
- XenForo.balloonCounterUpdate($('#VisitorExtraMenu_ConversationsCounter'), ajaxData._visitor_conversationsUnread);
- XenForo.balloonCounterUpdate($('#VisitorExtraMenu_AlertsCounter'), ajaxData._visitor_alertsUnread);
- XenForo.balloonCounterUpdate($('#VisitorExtraMenu_Counter'),
- (
- parseInt(ajaxData._visitor_conversationsUnread, 10) + parseInt(ajaxData._visitor_alertsUnread, 10)
- || 0
- ).toString()
- );
- var $alertPopup = $('.alerts.Popup')
- if (+ajaxData._visitor_alertsUnread) {
- // https://zelenka.guru/threads/4456536/
- if ($alertPopup.data('XenForo.PopupMenu'))
- $alertPopup.data('XenForo.PopupMenu').$menu.find('.tabs li').first().click()
- } else if (+ajaxData._visitor_alertsUnread && $alertPopup.is('.PopupOpen') && !ajaxData.error) {
- const popupMenu = $alertPopup.data('XenForo.PopupMenu')
- if (!popupMenu || !popupMenu.loading) return; // if not loaded yet
- XenForo.ajax(popupMenu.contentSrc, {}, ajaxData => {
- if (XenForo.hasResponseError(ajaxData)) return;
- popupMenu.$menu.find('ol').first().prepend($(ajaxData.templateHtml).find('li.Alert').filter(function () {
- const id = $(this).attr('id')
- return !id || !$('#' + id).length
- })).xfActivate()
- })
- }
- }
- if (ajaxData._manualDeferred) {
- XenForo.manualDeferredHandler();
- } else if (ajaxData._autoDeferred) {
- XenForo.runAutoDeferred();
- }
- }
- $(document).trigger(
- {
- type: 'XFAjaxSuccess',
- ajaxData: ajaxData,
- textStatus: textStatus
- });
- if (success) success.call(null, ajaxData, textStatus);
- };
- var referrer = window.location.href;
- if (referrer.match(/[^\x20-\x7f]/)) {
- var a = document.createElement('a');
- a.href = '';
- referrer = referrer.replace(a.href, XenForo.baseUrl());
- }
- if (data instanceof FormData)
- options = $.extend(true,
- {
- processData: false,
- contentType: false
- }, options);
- options = $.extend(true,
- {
- data: data,
- url: url,
- success: successCallback,
- type: 'POST',
- dataType: 'json',
- xhrFields: {
- withCredentials: true
- },
- error: function (xhr, textStatus, errorThrown) {
- if (xhr.readyState == 0) {
- return;
- }
- try {
- // attempt to pass off to success, if we can decode JSON from the response
- successCallback.call(null, $.parseJSON(xhr.responseText), textStatus);
- } catch (e) {
- // not valid JSON, trigger server error handler
- XenForo.handleServerError(xhr, textStatus, errorThrown, {
- success: successCallback,
- options: options,
- url: url,
- data: data
- });
- }
- },
- headers: { 'X-Ajax-Referer': referrer },
- timeout: 30000 // 30s
- }, options);
- // override standard extension, depending on dataType
- if (!options.data._xfResponseType) {
- switch (options.dataType) {
- case 'html':
- case 'json':
- case 'xml': {
- // pass _xfResponseType parameter to override default extension
- options.data = XenForo.ajaxDataPush(options.data, '_xfResponseType', options.dataType);
- break;
- }
- }
- }
- return $.ajax(options);
- },
- /**
- * Updates the total in one of the navigation balloons, showing or hiding if necessary
- *
- * @param jQuery $balloon
- * @param string counter
- */
- balloonCounterUpdate: function ($balloon, newTotal) {
- if ($balloon.length) {
- var $counter = $balloon.find('span.Total'),
- oldTotal = $counter.text();
- $counter.text(newTotal);
- if (!newTotal || newTotal == '0') {
- $balloon.fadeOut('fast', function () {
- $balloon.addClass('Zero').css('display', '');
- });
- } else {
- $balloon.fadeIn('fast', function () {
- $balloon.removeClass('Zero').css('display', '');
- var oldTotalInt = parseInt(oldTotal.replace(/[^\d]/, ''), 10),
- newTotalInt = parseInt(newTotal.replace(/[^\d]/, ''), 10),
- newDifference = newTotalInt - oldTotalInt;
- if (newDifference > 0 && $balloon.data('text')) {
- var $container = $balloon.closest('.Popup'),
- PopupMenu = $container.data('XenForo.PopupMenu'),
- $message;
- $message = $('<a />').css('cursor', 'pointer').html($balloon.data('text').replace(/%d/, newDifference)).click(function (e) {
- if ($container.is(':visible') && PopupMenu) {
- PopupMenu.$clicker.trigger('click');
- } else if ($container.find('a[href]').length) {
- window.location = XenForo.canonicalizeUrl($container.find('a[href]').attr('href'));
- }
- return false;
- });
- if (PopupMenu && !PopupMenu.menuVisible) {
- PopupMenu.resetLoader();
- }
- //XenForo.stackAlert($message, 10000, $balloon);
- }
- });
- }
- }
- },
- _manualDeferUrl: '',
- _manualDeferOverlay: false,
- _manualDeferXhr: false,
- manualDeferredHandler: function () {
- if (!XenForo._manualDeferUrl || XenForo._manualDeferOverlay) {
- return;
- }
- var processing = XenForo.phrases['processing'] || 'Processing',
- cancel = XenForo.phrases['cancel'] || 'Cancel',
- cancelling = XenForo.phrases['cancelling'] || 'Cancelling';
- var $html = $('<div id="ManualDeferOverlay" class="xenOverlay"><h2 class="titleBar">'
- + processing + '... '
- + '<a class="CancelDeferred button" data-cancelling="' + cancelling + '..." style="display:none">' + cancel + '</a></h2>'
- + '<span class="processingText">' + processing + '...</span><span class="close"></span></div>');
- $html.find('.CancelDeferred').click(function (e) {
- e.preventDefault();
- $.setCookie('cancel_defer', '1');
- $(this).text($(this).data('cancelling'));
- });
- $html.appendTo('body').overlay($.extend(true, {
- mask: {
- color: 'white',
- opacity: 0.77,
- loadSpeed: XenForo.speed.normal,
- closeSpeed: XenForo.speed.fast
- },
- closeOnClick: false,
- closeOnEsc: false,
- oneInstance: false
- }, XenForo._overlayConfig, { top: '20%' }));
- $html.overlay().load();
- XenForo._manualDeferOverlay = $html;
- $(document).trigger('PseudoAjaxStart');
- var closeOverlay = function () {
- XenForo._manualDeferOverlay.overlay().close();
- $('#ManualDeferOverlay').remove();
- XenForo._manualDeferOverlay = false;
- XenForo._manualDeferXhr = false;
- $(document).trigger('PseudoAjaxStop');
- $(document).trigger('ManualDeferComplete');
- };
- var fn = function () {
- XenForo._manualDeferXhr = XenForo.ajax(XenForo._manualDeferUrl, { execute: 1 }, function (ajaxData) {
- if (ajaxData && ajaxData.continueProcessing) {
- setTimeout(fn, 0);
- XenForo._manualDeferOverlay.find('span').text(ajaxData.status);
- var $cancel = XenForo._manualDeferOverlay.find('.CancelDeferred');
- if (ajaxData.canCancel) {
- $cancel.show();
- } else {
- $cancel.hide();
- }
- } else {
- closeOverlay();
- }
- }).fail(closeOverlay);
- };
- fn();
- },
- /**
- * Generic handler for server-level errors received from XenForo.ajax
- * Attempts to provide a useful error message.
- *
- * @param object XMLHttpRequest
- * @param string Response text
- * @param string Error thrown
- * @param object XenForo.ajax params
- *
- * @return boolean False
- */
- handleServerError: function (xhr, responseText, errorThrown, params) {
- var invalidDfIdRegexs = [
- /^<html>\n<body>\n<script type="text\/javascript" src="\/aes\.js" ><\/script>/,
- /^<html>\n<body>\n\x3Cscript type="text\/javascript" src="\/aes.js" >\x3C\/script>\n/,
- /^<!doctype html><html><head><script src="\/process-[^"]+.js"><\/script><\/head><body><script>window\.onload=function\(\)\{process\(\);\}<\/script><noscript><p>Please enable JavaScript and Cookies in your browser\.<\/p><\/noscript><\/body><\/html>$/
- ]
- var errorPageRegexs = [
- /^<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset="utf-8" \/>\r\n<title>Error 50[0-9]<\/title>\r\n<style type="text\/css">/,
- /^<!DOCTYPE html>\n<html>\n<head>\n<meta charset="utf-8" \/>\n<title>Error 50[0-9]<\/title>\n<style type="text\/css">/,
- /^<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<meta charset="utf-8" \/>\r\n<title>Site Maintenance<\/title>\r\n<style type="text\/css">/,
- /^<!DOCTYPE html>\n<html>\n<head>\n<meta charset="utf-8" \/>\n<title>Site Maintenance<\/title>\n<style type="text\/css">/
- ]
- // handle timeout and parse error before attempting to decode an error
- console.log(responseText)
- switch (responseText) {
- case 'abort': {
- return false;
- }
- case 'timeout': {
- XenForo.alert(
- XenForo.phrases.server_did_not_respond_in_time_try_again,
- XenForo.phrases.following_error_occurred + ':'
- );
- return false;
- }
- case 'parsererror': {
- var needToUpdateDfId = false
- for (let i = 0; i < invalidDfIdRegexs.length; i++)
- if (invalidDfIdRegexs[i].test(xhr.responseText)) {
- needToUpdateDfId = true
- break
- }
- if (params && !params.options.disableDfIdRefresh && needToUpdateDfId) {
- console.log('df id refresh...')
- $('body').append(
- $('<iframe src="' + xhr.responseText.split('window.location.href="')[1].split('"')[0] + '" style="display: none;"></iframe>')
- .on('load', function () {
- $(this).remove()
- params.options.disableDfIdRefresh = true
- XenForo.ajax(params.url, params.data, params.success, params.options)
- })
- )
- } else {
- console.error('PHP ' + xhr.responseText);
- XenForo.alert('The server responded with an error. The error message is in the JavaScript console.');
- }
- return false;
- }
- case 'notmodified':
- case 'error': {
- if (!xhr || !xhr.responseText) {
- // this is likely a user cancellation, so just return
- return false;
- }
- var needToParse = false
- for (let i = 0; i < errorPageRegexs.length; i++)
- if (errorPageRegexs[i].test(xhr.responseText)) {
- needToParse = true
- break
- }
- if (needToParse) {
- XenForo.alert($(xhr.responseText).closest('article').get(0).innerText.trim().replace('\n\t', '<br>'))
- return false;
- }
- break;
- }
- }
- var contentTypeHeader = xhr.getResponseHeader('Content-Type'),
- contentType = false,
- data;
- if (contentTypeHeader) {
- switch (contentTypeHeader.split(';')[0]) {
- case 'application/json': {
- contentType = 'json';
- break;
- }
- case 'text/html': {
- contentType = 'html';
- break;
- }
- default: {
- if (xhr.responseText.substr(0, 1) == '{') {
- contentType = 'json';
- } else if (xhr.responseText.substr(0, 9) == '<!DOCTYPE') {
- contentType = 'html';
- }
- }
- }
- }
- if (contentType == 'json' && xhr.responseText.substr(0, 1) == '{') {
- // XMLHttpRequest response is probably JSON
- try {
- data = $.parseJSON(xhr.responseText);
- } catch (e) {
- }
- if (data) {
- XenForo.hasResponseError(data, xhr.status);
- } else {
- XenForo.alert(xhr.responseText, XenForo.phrases.following_error_occurred + ':');
- }
- } else {
- // XMLHttpRequest is some other type...
- XenForo.alert(xhr.responseText, XenForo.phrases.following_error_occurred + ':');
- }
- return false;
- },
- /**
- * Checks for the presence of an 'error' key in the provided data
- * and displays its contents if found, using an alert.
- *
- * @param object ajaxData
- * @param integer HTTP error code (optional)
- *
- * @return boolean|string Returns the error string if found, or false if not found.
- */
- hasResponseError: function (ajaxData, httpErrorCode) {
- if (typeof ajaxData != 'object') {
- XenForo.alert('Response not JSON!'); // debug info, no phrasing
- return true;
- }
- if (ajaxData.errorTemplateHtml) {
- new XenForo.ExtLoader(ajaxData, function (data) {
- var $overlayHtml = XenForo.alert(
- ajaxData.errorTemplateHtml,
- XenForo.phrases.following_error_occurred + ':'
- );
- if ($overlayHtml) {
- $overlayHtml.find('div.errorDetails').removeClass('baseHtml');
- if (ajaxData.errorOverlayType) {
- $overlayHtml.closest('.errorOverlay').removeClass('errorOverlay').addClass(ajaxData.errorOverlayType);
- }
- }
- });
- return ajaxData.error || true;
- } else if (ajaxData.error !== undefined) {
- // TODO: ideally, handle an array of errors
- if (typeof ajaxData.error === 'object') {
- var key;
- for (key in ajaxData.error) {
- break;
- }
- ajaxData.error = ajaxData.error[key];
- }
- XenForo.alert(
- ajaxData.error + '\n'
- + (ajaxData.traceHtml !== undefined ? '<ol class="traceHtml">\n' + ajaxData.traceHtml + '</ol>' : ''),
- XenForo.phrases.following_error_occurred + ':'
- );
- return ajaxData.error;
- } else if (ajaxData.status == 'ok' && ajaxData.message) {
- XenForo.alert(ajaxData.message, '', 4000);
- return true;
- } else {
- return false;
- }
- },
- /**
- * Checks that the supplied ajaxData has a key that can be used to create a jQuery object
- *
- * @param object ajaxData
- * @param string key to look for (defaults to 'templateHtml')
- *
- * @return boolean
- */
- hasTemplateHtml: function (ajaxData, templateKey) {
- templateKey = templateKey || 'templateHtml';
- if (!ajaxData[templateKey]) {
- return false;
- }
- if (typeof (ajaxData[templateKey].search) == 'function') {
- return (ajaxData[templateKey].search(/\S+/) !== -1);
- } else {
- return true;
- }
- },
- /**
- * Creates an overlay using the given HTML
- *
- * @param jQuery Trigger element
- * @param string|jQuery HTML
- * @param object Extra options for overlay, will override defaults if specified
- *
- * @return jQuery Overlay API
- */
- createOverlay: function ($trigger, templateHtml, extraOptions) {
- var $overlay = null,
- $templateHtml = null,
- api = null,
- scripts = [],
- i;
- if (templateHtml instanceof jQuery && templateHtml.is('.xenOverlay')) {
- // this is an object that has already been initialised
- $overlay = templateHtml.appendTo('body');
- $templateHtml = templateHtml;
- } else {
- const parsed = new DOMParser().parseFromString(templateHtml, 'text/html')
- const scriptEls = Array.from($(parsed).find('script').filter(function () {
- if (!$(this).text().trim().length) return false
- const type = $(this).attr('type')
- if (type && type !== 'text/javascript') return false
- return true
- }))
- scripts = scriptEls.map(el => el.innerHTML)
- for (const el of scriptEls)
- templateHtml = templateHtml.replace(el.outerHTML, '')
- $templateHtml = $(templateHtml);
- // add a header to the overlay, unless instructed otherwise
- if (!$templateHtml.is('.NoAutoHeader')) {
- if (extraOptions && extraOptions.title) {
- $('<h2 class="heading h1" />')
- .html(extraOptions.title)
- .prependTo($templateHtml);
- }
- }
- // add a cancel button to the overlay, if the overlay is a .formOverlay, has a .submitUnit but has no :reset button
- if ($templateHtml.is('.formOverlay')) {
- if ($templateHtml.find('.submitUnit').length) {
- if (!$templateHtml.find('.submitUnit :reset').length) {
- $templateHtml.find('.submitUnit .button:last')
- .after($('<input type="reset" class="button OverlayCloser" />').val(XenForo.phrases.cancel))
- .after(' ');
- }
- }
- }
- // create an overlay container, add the activated template to it and append it to the body.
- $overlay = $('<div class="xenOverlay __XenForoActivator" />')
- .addClass($(templateHtml).data('overlayclass')) // if content defines data-overlayClass, apply the value to the overlay as a class.
- .append($templateHtml);
- ($trigger || $overlay).one('onBeforeLoad', function () {
- for (i = 0; i < scripts.length; i++) {
- $.globalEval(scripts[i]);
- }
- $overlay.xfActivate()
- })
- }
- var linkRegEx = /([^a-z0-9@-]|^)((?:https?:\/\/|www\.)[^\s"<>\{\}\[\]`_,\(\)]+)/ig
- $overlay.find(':not(a):not(textarea):not(script)').each(function () {
- var $el = $(this)
- if ($el.children().length == 0)
- $el.html($el.html().replace(linkRegEx, function (matched, c, link) {
- return `${c}<a href="${link}">${link}</a>`
- }))
- })
- if (extraOptions) {
- // add {effect}Effect class to overlay container if necessary
- if (extraOptions.effect) {
- $overlay.addClass(extraOptions.effect + 'Effect');
- }
- // add any extra class name defined in extraOptions
- if (extraOptions.className) {
- $overlay.addClass(extraOptions.className);
- delete (extraOptions.className);
- }
- if (extraOptions.noCache) {
- extraOptions.onClose = function () {
- $overlay.empty().remove();
- };
- }
- }
- // add an overlay closer if one does not already exist
- let exists = !$overlay.find('.OverlayCloser').length;
- if (exists) {
- $overlay.prepend('<a class="close OverlayCloser"></a>');
- }
- $overlay.find('.OverlayCloser').toArray().forEach($element => {
- if (!$element.href) {
- exists = true;
- }
- })
- if (!exists) {
- $overlay.prepend('<a class="close OverlayCloser"></a>');
- }
- $overlay.find('.OverlayCloser').click(function (e) {
- e.stopPropagation();
- });
- // if no trigger was specified (automatic popup), then activate the overlay instead of the trigger
- $trigger = $trigger || $overlay;
- var windowHeight = $(window).height();
- var fixed = !(
- ($.browser.msie && $.browser.version <= 6) // IE6 doesn't support position: fixed;
- || XenForo.isTouchBrowser()
- || $(window).width() <= 600 // overlay might end up especially tall
- || windowHeight <= 550
- || $overlay.outerHeight() >= .9 * windowHeight
- );
- if ($templateHtml.is('.NoFixedOverlay')) {
- fixed = false;
- }
- // activate the overlay
- var $modal = new XenForo.Modal($overlay, $.extend(true, {
- close: '.OverlayCloser',
- closeSpeed: XenForo.speed.slow,
- fixed: fixed,
- trigger: $trigger
- }, XenForo._overlayConfig, extraOptions))
- $trigger.bind(
- {
- onBeforeLoad: function (e) {
- $(document).triggerHandler('OverlayOpening');
- $('.MenuOpened').removeClass('MenuOpened')
- $overlay.find('.Popup').each(function ($popup) {
- var $data = $(this).data('XenForo.PopupMenu')
- if ($data) $data.$menu.css('z-index', 11114)
- })
- if (XenForo.isTouchBrowser()) {
- const height = $overlay.outerHeight();
- if (height < $(window).height() * 0.9) {
- $overlay.addClass('slim');
- }
- }
- },
- onLoad: function (e) {
- var api = $(this).data('overlay'),
- $overlay = api.getOverlay(),
- scroller = $overlay.find('.OverlayScroller').get(0),
- resizeClose = null;
- if ($overlay.css('position') == 'absolute') {
- $overlay.find('.overlayScroll').removeClass('overlayScroll');
- }
- // timeout prevents flicker in FF
- if (scroller) {
- setTimeout(function () {
- scroller.scrollIntoView(true);
- }, 0);
- }
- // autofocus the first form element in a .formOverlay
- var $focus = $overlay.find('form').find('input[autofocus], textarea[autofocus], select[autofocus], .AutoFocus').first();
- if ($focus.length) {
- $focus.focus();
- } else {
- $overlay.find('form').find('input:not([type=hidden], [type=file]), textarea, select, button, .submitUnit a.button').first().focus();
- }
- // hide on window resize
- if (api.getConf().closeOnResize) {
- resizeClose = function () {
- console.info('Window resize, close overlay!');
- api.close();
- };
- $(window).one('resize', resizeClose);
- // remove event when closing the overlay
- $trigger.one('onClose', function () {
- $(window).unbind('resize', resizeClose);
- });
- }
- $(document).triggerHandler('OverlayOpened');
- },
- onBeforeClose: function (e) {
- $overlay.find('.Popup').each(function () {
- var PopupMenu = $(this).data('XenForo.PopupMenu');
- if (PopupMenu.hideMenu) {
- PopupMenu.hideMenu(e, true);
- }
- });
- $('.autoCompleteList').each(function () {
- let autoCompleteList = $(this).data('XenForo.AutoCompleteResults')
- if (autoCompleteList && autoCompleteList.hideResults) {
- autoCompleteList.hideResults()
- }
- })
- $('.autoCompleteListSmilies').each(function () {
- let autoCompleteList = $(this).data('XenForo.AutoSmiliesCompleteResults')
- if (autoCompleteList && autoCompleteList.hideResults) {
- autoCompleteList.hideResults()
- }
- })
- }
- });
- api = $trigger.data('overlay');
- $overlay.data('overlay', api);
- return api;
- },
- /**
- * Present the user with a pop-up, modal message that they must confirm
- *
- * @param string Message
- * @param string Message type (error, info, redirect)
- * @param integer Timeout (auto-close after this period)
- * @param function Callback onClose
- */
- alert: function (message, messageType, timeOut, onClose) {
- message = String(message || 'Unspecified error');
- var key = message.replace(/[^a-z0-9_]/gi, '_') + parseInt(timeOut),
- $overlayHtml;
- if (XenForo._OverlayCache[key] === undefined) {
- if (timeOut) {
- $overlayHtml = ''
- + '<div class="xenOverlay animated slideInLeft faster timedMessage">'
- + '<div class="content baseHtml">'
- + message
- + '<span class="close"></span>'
- + '</div>'
- + '</div>';
- new XenForo.TimedMessage($overlayHtml, timeOut, onClose)
- } else {
- $overlayHtml = $(''
- + '<div class="errorOverlay">'
- + '<a class="close OverlayCloser"></a>'
- + '<h2 class="heading">' + (messageType || XenForo.phrases.following_error_occurred) + '</h2>'
- + '<div class="baseHtml errorDetails"></div>'
- + '</div>'
- );
- $overlayHtml.find('div.errorDetails').html(message);
- XenForo._OverlayCache[key] = XenForo.createOverlay(null, $overlayHtml, {
- onLoad: function () {
- var el = $('input:button.close, button.close', document.getElementById(key)).get(0);
- if (el) {
- el.focus();
- }
- },
- onClose: function () {
- delete XenForo._OverlayCache[key]
- onClose && onClose()
- }
- });
- XenForo._OverlayCache[key].load();
- const $timedMessage = $('body > .timedMessage')
- if ($timedMessage.length) {
- $timedMessage.fadeOut(250, function () {
- $(this).remove()
- })
- }
- }
- }
- return $overlayHtml;
- },
- /**
- * Shows a mini timed alert message, much like the OS X notifier 'Growl'
- *
- * @param string message
- * @param integer timeOut Leave empty for a sticky message
- * @param jQuery Counter balloon
- */
- stackAlert: function (message, timeOut, $balloon) {
- var $message = $('<li class="stackAlert DismissParent"><div class="stackAlertContent">'
- + '<span class="helper"></span>'
- + '<a class="DismissCtrl"></a>'
- + '</div></li>'),
- $container = $('#StackAlerts');
- if (!$container.length) {
- $container = $('<ul id="StackAlerts"></ul>').appendTo('body');
- }
- if ((message instanceof jQuery) == false) {
- message = $('<span>' + message + '</span>');
- }
- message.appendTo($message.find('div.stackAlertContent'));
- function removeMessage() {
- $message.xfFadeUp(XenForo.speed.slow, function () {
- $(this).empty().remove();
- if (!$container.children().length) {
- $container.hide();
- }
- });
- }
- function removeMessageAndScroll(e) {
- if ($balloon && $balloon.length) {
- $balloon.get(0).scrollIntoView(true);
- }
- removeMessage();
- }
- $message
- .hide()
- .prependTo($container.show())
- .fadeIn(XenForo.speed.normal, function () {
- if (timeOut > 0) {
- setTimeout(removeMessage, timeOut);
- }
- });
- $message.find('a').click(removeMessageAndScroll);
- return $message;
- },
- /**
- * Adjusts all animation speeds used by XenForo
- *
- * @param integer multiplier - set to 0 to disable all animation
- */
- setAnimationSpeed: function (multiplier) {
- var ieSpeedAdjust, s, index;
- for (index in XenForo.speed) {
- s = XenForo.speed[index];
- if ($.browser.msie) {
- // if we are using IE, change the animation lengths for a smoother appearance
- if (s <= 100) {
- ieSpeedAdjust = 2;
- } else if (s > 800) {
- ieSpeedAdjust = 1;
- } else {
- ieSpeedAdjust = 1 + 100 / s;
- }
- XenForo.speed[index] = s * multiplier * ieSpeedAdjust;
- } else {
- XenForo.speed[index] = s * multiplier;
- }
- }
- },
- /**
- * Generates a unique ID for an element, if required
- *
- * @param object HTML element (optional)
- *
- * @return string Unique ID
- */
- uniqueId: function (element) {
- if (!element) {
- return 'XenForoUniq' + XenForo._uniqueIdCounter++;
- } else {
- return $(element).uniqueId().attr('id');
- }
- },
- redirect: function (url) {
- url = XenForo.canonicalizeUrl(url);
- if (url == window.location.href) {
- window.location.reload();
- } else {
- window.location = url;
- var destParts = url.split('#'),
- srcParts = window.location.href.split('#');
- if (destParts[1]) // has a hash
- {
- if (destParts[0] == srcParts[0]) {
- // destination has a hash, but going to the same page
- window.location.reload();
- }
- }
- }
- },
- canonicalizeUrl: function (url, baseHref) {
- if (url.indexOf('/') == 0) {
- return url;
- } else if (url.match(/^(https?:|ftp:|mailto:)/i)) {
- return url;
- } else {
- if (!baseHref) {
- baseHref = XenForo.baseUrl();
- }
- if (typeof baseHref != 'string') {
- baseHref = '';
- }
- return baseHref + url;
- }
- },
- _baseUrl: false,
- baseUrl: function () {
- if (XenForo._baseUrl === false) {
- var b = document.createElement('a'), $base = $('base');
- b.href = '';
- XenForo._baseUrl = (b.href.match(/[^\x20-\x7f]/) && $base.length) ? $base.attr('href') : b.href;
- if (!$base.length) {
- XenForo._baseUrl = XenForo._baseUrl.replace(/\?.*$/, '').replace(/\/[^\/]*$/, '/');
- }
- }
- return XenForo._baseUrl;
- },
- /**
- * Adds a trailing slash to a string if one is not already present
- *
- * @param string
- */
- trailingSlash: function (string) {
- if (string.substr(-1) != '/') {
- string += '/';
- }
- return string;
- },
- /**
- * Escapes a string so it can be inserted into a RegExp without altering special characters
- *
- * @param string
- *
- * @return string
- */
- regexQuote: function (string) {
- return (string + '').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!<>\|\:])/g, "\\$1");
- },
- /**
- * Escapes HTML into plain text
- *
- * @param string
- *
- * @return string
- */
- htmlspecialchars: function (string) {
- return (String(string) || '')
- .replace(/&/g, '&')
- .replace(/"/g, '"')
- .replace(/</g, '<')
- .replace(/>/g, '>');
- },
- htmlEntityDecode: function (string) {
- return (String(string) || '')
- .replace(/"/g, '"')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/&/g, '&');
- },
- /**
- * Determines whether the current page is being viewed in RTL mode
- *
- * @return boolean
- */
- isRTL: function () {
- if (XenForo.RTL === undefined) {
- var dir = $('html').attr('dir');
- XenForo.RTL = (dir && dir.toUpperCase() == 'RTL') ? true : false;
- }
- return XenForo.RTL;
- },
- /**
- * Switches instances of 'left' with 'right' and vice-versa in the input string.
- *
- * @param string directionString
- *
- * @return string
- */
- switchStringRTL: function (directionString) {
- if (XenForo.isRTL()) {
- directionString = directionString.replace(/left/i, 'l_e_f_t');
- directionString = directionString.replace(/right/i, 'left');
- directionString = directionString.replace('l_e_f_t', 'right');
- }
- return directionString;
- },
- /**
- * Switches the x-coordinate of the input offset array
- * @param offsetArray
- * @return string
- */
- switchOffsetRTL: function (offsetArray) {
- if (XenForo.isRTL() && !isNaN(offsetArray[1])) {
- offsetArray[1] = offsetArray[1] * -1;
- }
- return offsetArray;
- },
- /**
- * Checks whether or not a tag is a list container
- *
- * @param jQuery Tag
- *
- * @return boolean
- */
- isListTag: function ($tag) {
- return ($tag.tagName == 'ul' || $tag.tagName == 'ol');
- },
- /**
- * Checks that the value passed is a numeric value, even if its actual type is a string
- *
- * @param mixed Value to be checked
- *
- * @return boolean
- */
- isNumeric: function (value) {
- return (!isNaN(value) && (value - 0) == value && value.length > 0);
- },
- /**
- * Helper to check that an attribute value is 'positive'
- *
- * @param scalar Value to check
- *
- * @return boolean
- */
- isPositive: function (value) {
- switch (String(value).toLowerCase()) {
- case 'on':
- case 'yes':
- case 'true':
- case '1':
- return true;
- default:
- return false;
- }
- },
- /**
- * Converts the first character of a string to uppercase.
- *
- * @param string
- *
- * @return string
- */
- ucfirst: function (string) {
- return string.charAt(0).toUpperCase() + string.substr(1);
- },
- /**
- * Replaces any existing avatars for the given user on the page
- *
- * @param integer user ID
- * @param array List of avatar urls for the user, keyed with size code
- * @param boolean Include crop editor image
- */
- updateUserAvatars: function (userId, avatarUrls, andEditor) {
- console.log('Replacing visitor avatars on page: %o', avatarUrls);
- $.each(avatarUrls, function (sizeCode, avatarUrl) {
- var sizeClass = '.Av' + userId + sizeCode + (andEditor ? '' : ':not(.AvatarCropControl)');
- // .avatar > img
- $(sizeClass).find('img').attr('src', avatarUrl);
- // .avatar > span.img
- $(sizeClass).find('span.img').css('background-image', 'url(' + avatarUrl + ')');
- // visitor
- $('.navTab--visitorAvatar').attr('src', avatarUrl);
- });
- },
- getEditorInForm: function (form, extraConstraints) {
- var $form = $(form),
- $textarea = $form.find('textarea.MessageEditor' + (extraConstraints || '')).first();
- if ($textarea.length) {
- if ($textarea.prop('disabled')) {
- return $form.find('.bbCodeEditorContainer textarea' + (extraConstraints || ''));
- } else if ($textarea.data('redactor')) {
- return $textarea.data('redactor');
- } else {
- return $textarea;
- }
- }
- return false;
- },
- /**
- * Returns the name of the tag that should be animated for page scrolling
- *
- * @return string
- */
- getPageScrollTagName: function () {
- // if we're currently scrolled, we should be able to determine where this is from
- if ($('html').scrollTop() > 0) {
- return 'html';
- } else if ($('body').scrollTop() > 0) {
- return 'body';
- }
- // otherwise, need to determine based on browser - Webkit uses body, but Chrome 61 has flipped to HTML
- var match = navigator.userAgent.match(/Chrome\/([0-9]+)/i);
- if (match && parseInt(match[1], 10) >= 61) {
- return 'html';
- } else if ($.browser.webkit) {
- return 'body';
- } else {
- return 'html';
- }
- },
- /**
- * Determines whether or not we are working with a touch-based browser
- *
- * @return boolean
- */
- isTouchBrowser: isTouchBrowser,
- scriptLoader: class {
- /**
- * Lazy-loads Javascript files
- * @param {string} url
- * @return {Promise<void>}
- */
- static async loadScriptAsync(url) {
- if (url in XenForo._loadedScripts) return;
- const script = await fetch(url, { mode: 'cors' }).then(r => r.text())
- $('<script/>').text(script).appendTo('head').remove()
- XenForo._loadedScripts[url] = true
- }
- static async loadScriptsAsync(urls) {
- const promises = []
- for (const url of urls) {
- if (url in XenForo._loadedScripts) continue;
- promises.push(fetch(url, { mode: 'cors' }).then(r => r.text()).then(script => ([url, script])))
- }
- const scripts = await Promise.all(promises)
- for (const [url, script] of scripts) {
- $('<script/>').text(script).appendTo('head').remove()
- XenForo._loadedScripts[url] = true
- }
- }
- /**
- * Lazy-loads Javascript files
- * @deprecated use loadScriptAsync
- * @param {string} url
- * @param {() => void} success
- * @param {(err: any) => void} failure
- * @return {void}
- */
- static loadScript(url, success, failure) {
- this.loadScriptAsync(url).then(success, failure)
- }
- /**
- * Lazy-loads CSS templates
- * @param {string[]} css
- * @param {string} urlTemplate
- * @return {Promise<void>}
- */
- static async loadCssAsync(css, urlTemplate) {
- const requiredCss = css.filter(t => !(t in XenForo._loadedScripts))
- if (!requiredCss.length) return;
- const url = urlTemplate.replace('__sentinel__', requiredCss.join(','))
- const style = await fetch(url, { mode: 'cors' }).then(r => r.text())
- for (const template of requiredCss) {
- XenForo._loadedScripts[template] = true
- console.log('ScriptLoader: Loaded css, %s', template)
- }
- const baseHref = XenForo.baseUrl()
- const patchedStyle = style.replace(/(url\((?:"|')?)([^"')]+)((?:"|')?\))/gi, (all, front, url, back) => {
- // if URL already absolute OR looks like a data URI do not prefix with baseHref
- if (!url.match(/^https?:|\//i) && !url.match(/^data:/i))
- url = baseHref + url
- return front + url + back
- })
- $('<style/>').text(patchedStyle).appendTo('head')
- }
- /**
- * Lazy-loads CSS templates
- * @deprecated use loadCss
- * @param {string[]} css
- * @param {string} urlTemplate
- * @param {() => void} success
- * @param {(err: any) => void} failure
- * @return {void}
- */
- static loadCss(css, urlTemplate, success, failure) {
- this.loadCssAsync(css, urlTemplate).then(success, failure)
- }
- /**
- * Lazy-loads JavaScript and CSS
- * @param {({ type: 'css', list: string[]} | { type: 'js', url: string })[]} data
- * @param {string} urlTemplate
- * @return {Promise<void>}
- */
- static loadMultipleAsync(data, urlTemplate = `css.php?css=__sentinel__&style=9&_v=${XenForo._jsVersion}`) {
- /** @type {Promise<void>[]} */
- const promises = []
- for (const item of data) {
- switch (item.type) {
- case 'js': promises.push(this.loadScriptAsync(item.url)); break
- case 'css': promises.push(this.loadCssAsync(item.list, urlTemplate))
- }
- }
- return Promise.all(promises)
- }
- /**
- * Lazy-loads JavaScript and CSS
- * @deprecated use loadMultipleAsync
- * @param {({ type: 'css', list: string[]} | { type: 'js', url: string })[]} data
- * @param {() => void} callback
- * @param {string | undefined} urlTemplate
- * @return {void}
- */
- static loadMultiple(data, callback, urlTemplate) {
- this.loadMultipleAsync(data, urlTemplate).then(callback, err => {
- console.warn('ScriptLoader.loadMultiple error:', err)
- })
- }
- }
- });
- // *********************************************************************
- /**
- * Loads the requested list of javascript and css files
- * Before firing the specified callback.
- */
- XenForo.ExtLoader = class {
- /**
- * @template T
- * @param {T} data Ajax data
- * @param {(T) => void} success Success callback
- * @param {(T) => void} failure Error callback
- */
- constructor(data = {}, success = () => { }, failure = () => { }) {
- /** @type {Promise<void>[]} */
- const promises = []
- if (typeof data?.css?.urlTemplate === 'string' && Array.isArray(data?.css?.stylesheets) && data.css.stylesheets.length) {
- promises.push(XenForo.scriptLoader.loadCssAsync(data.css.stylesheets, data.css.urlTemplate))
- }
- if (Array.isArray(data?.js)) {
- promises.push(XenForo.scriptLoader.loadScriptsAsync(data.js.reverse()))
- }
- Promise.all(promises).then(() => { success(data) }, err => {
- console.warn('ExtLoader error:', err)
- failure(data)
- })
- }
- }
- // *********************************************************************
- /**
- * Instance of XenForo.TimestampRefresh
- *
- * @var XenForo.TimestampRefresh
- */
- XenForo._TimestampRefresh = null;
- /**
- * Allows date/time stamps on the page to be displayed as relative to now, and auto-refreshes periodically
- */
- XenForo.TimestampRefresh = function () {
- this.__construct();
- };
- XenForo.TimestampRefresh.prototype =
- {
- __construct: function () {
- this.active = this.activate();
- $(document).bind('XenForoWindowFocus', $.context(this, 'focus'));
- },
- /**
- * Runs on window.focus, activates the system if deactivated
- *
- * @param event e
- */
- focus: function (e) {
- if (!this.active) {
- this.activate(true);
- }
- },
- /**
- * Runs a refresh, then refreshes again every 60 seconds
- *
- * @param boolean Refresh instantly
- *
- * @return integer Refresh interval or something...
- */
- activate: function (instant) {
- if (instant) {
- this.refresh();
- }
- return this.active = window.setInterval($.context(this, 'refresh'), 60 * 1000); // one minute
- },
- /**
- * Halts timestamp refreshes
- *
- * @return boolean false
- */
- deactivate: function () {
- window.clearInterval(this.active);
- return this.active = false;
- },
- /**
- * Date/Time output updates
- */
- refresh: function (element, force) {
- if (!XenForo._hasFocus && !force) {
- return this.deactivate();
- }
- if ($.browser.msie && $.browser.version <= 6) {
- return;
- }
- var $elements = $('abbr.DateTime[data-time]', element),
- pageOpenTime = (new Date().getTime() / 1000),
- pageOpenLength = pageOpenTime - XenForo._pageLoadTime,
- serverTime = XenForo.serverTimeInfo.now,
- today = XenForo.serverTimeInfo.today,
- todayDow = XenForo.serverTimeInfo.todayDow,
- yesterday, week, dayOffset,
- i, $element, thisTime, thisDiff, thisServerTime, interval, calcDow;
- if (serverTime + pageOpenLength > today + 86400) {
- // day has changed, need to adjust
- dayOffset = Math.floor((serverTime + pageOpenLength - today) / 86400);
- today += dayOffset * 86400;
- todayDow = (todayDow + dayOffset) % 7;
- }
- yesterday = today - 86400;
- week = today - 6 * 86400;
- var rtlMarker = XenForo.isRTL() ? '\u200F' : '';
- for (i = 0; i < $elements.length; i++) {
- $element = $($elements[i]);
- // set the original value of the tag as its title
- if (!$element.attr('title')) {
- $element.attr('title', $element.text());
- }
- thisDiff = parseInt($element.data('diff'), 10);
- thisTime = parseInt($element.data('time'), 10);
- thisServerTime = thisTime + thisDiff;
- if (thisServerTime > serverTime + pageOpenLength) {
- thisServerTime = Math.floor(serverTime + pageOpenLength);
- }
- interval = serverTime - thisServerTime + thisDiff + pageOpenLength;
- if (interval < 0) {
- // date in the future
- } else if (interval <= 60) {
- $element.text(XenForo.phrases.a_moment_ago);
- } else if (interval <= 120) {
- $element.text(XenForo.phrases.one_minute_ago);
- } else if (interval < 3600) {
- $element.text(XenForo.phrases.x_minutes_ago
- .replace(/%minutes%/, Math.floor(interval / 60)));
- } else if (thisTime >= today) {
- $element.text(XenForo.phrases.today_at_x
- .replace(/%time%/, $element.attr('data-timestring'))); // must use attr for string value
- } else if (thisTime >= yesterday) {
- $element.text(XenForo.phrases.yesterday_at_x
- .replace(/%time%/, $element.attr('data-timestring'))); // must use attr for string value
- } else if (thisTime >= week) {
- calcDow = todayDow - Math.ceil((today - thisTime) / 86400);
- if (calcDow < 0) {
- calcDow += 7;
- }
- $element.text(rtlMarker + XenForo.phrases.day_x_at_time_y
- .replace('%day%', XenForo.phrases['day' + calcDow])
- .replace(/%time%/, $element.attr('data-timestring')) // must use attr for string value
- );
- } else {
- $element.text(rtlMarker + $element.attr('data-datestring')); // must use attr for string value
- }
- }
- }
- };
- // *********************************************************************
- /**
- * Periodically refreshes all CSRF tokens on the page
- */
- XenForo.CsrfRefresh = function () {
- this.__construct();
- };
- XenForo.CsrfRefresh.prototype =
- {
- __construct: function () {
- this.activate();
- $(document).bind('XenForoWindowFocus', $.context(this, 'focus'));
- },
- /**
- * Runs on window focus, activates the system if deactivated
- *
- * @param event e
- */
- focus: function (e) {
- if (!this.active) {
- this.activate(true);
- }
- },
- /**
- * Runs a refresh, then refreshes again every hour
- *
- * @param boolean Refresh instantly
- *
- * @return integer Refresh interval or something...
- */
- activate: function (instant) {
- if (instant) {
- this.refresh();
- }
- this.active = window.setInterval($.context(this, 'refresh'), 50 * 60 * 1000); // 50 minutes
- return this.active;
- },
- /**
- * Halts csrf refreshes
- */
- deactivate: function () {
- window.clearInterval(this.active);
- this.active = false;
- },
- /**
- * Updates all CSRF tokens
- */
- refresh: function () {
- if (!XenForo._csrfRefreshUrl) {
- return;
- }
- if (!XenForo._hasFocus) {
- this.deactivate();
- return;
- }
- XenForo.ajax(
- XenForo._csrfRefreshUrl,
- '',
- function (ajaxData, textStatus) {
- if (!ajaxData || ajaxData.csrfToken === undefined) {
- return false;
- }
- var tokenInputs = $('input[name=_xfToken]').val(ajaxData.csrfToken);
- XenForo._csrfToken = ajaxData.csrfToken;
- if (tokenInputs.length) {
- console.log('XenForo CSRF token updated in %d places (%s)', tokenInputs.length, ajaxData.csrfToken);
- }
- $(document).trigger(
- {
- type: 'CSRFRefresh',
- ajaxData: ajaxData
- });
- },
- { error: false, global: false }
- );
- }
- };
- // *********************************************************************
- /**
- * Stores the id of the currently active popup menu group
- *
- * @var string
- */
- XenForo._PopupMenuActiveGroup = null;
- /**
- * Popup menu system.
- *
- * Requires:
- * <el class="Popup">
- * <a rel="Menu">control</a>
- * <el class="Menu {Left} {Hider}">menu content</el>
- * </el>
- *
- * * .Menu.Left causes orientation of menu to reverse, away from scrollbar
- * * .Menu.Hider causes menu to appear over control instead of below
- *
- * @param jQuery *.Popup container element
- */
- XenForo.PopupMenu = function ($container) {
- this.__construct($container);
- };
- XenForo.PopupMenu.prototype =
- {
- __construct: function ($container) {
- // the container holds the control and the menu
- this.$container = $container;
- if (!$(".MenuContainer").length) {
- $("<div class='MenuContainer' />").appendTo('body')
- }
- // take the menu, which will be a sibling of the control, and append/move it to the end of the body
- this.$menu = this.$container.find('.Menu').first().appendTo('.MenuContainer');
- this.$menu.data('XenForo.PopupMenu', this);
- this.menuVisible = false;
- // check that we have the necessary elements
- if (!this.$menu.length) {
- console.warn('Unable to find menu for Popup %o', this.$container);
- return false;
- }
- // add a unique id to the menu
- this.$menu.id = XenForo.uniqueId(this.$menu);
- // variables related to dynamic content loading
- this.contentSrc = this.$menu.data('contentsrc');
- this.contentDest = this.$menu.data('contentdest');
- this.loading = null;
- this.unreadDisplayTimeout = null;
- this.newlyOpened = false;
- // bind events to the menu control
- this.$clicker = $container.find('[rel="Menu"]').first().click($.context(this, 'controlClick'));
- this.$clicker.data('XenForo.PopupMenu', this);
- if (!XenForo.isTouchBrowser() && !$container.hasClass('DisableHover')) {
- this.$clicker.mouseover($.context(this, 'controlHover')).hoverIntent(
- {
- sensitivity: 1,
- interval: 50,
- timeout: 0,
- over: $.context(this, 'controlHoverIntent'),
- out: function () {
- }
- });
- }
- this.$control = this.addPopupGadget(this.$clicker);
- // the popup group for this menu, if specified
- this.popupGroup = this.$control.closest('[data-popupgroup]').data('popupgroup');
- //console.log('Finished popup menu for %o', this.$control);
- this.useInfiniteScroll = $container.is('.alerts')
- },
- addPopupGadget: function ($control) {
- if (!$control.hasClass('NoPopupGadget') && !$control.hasClass('SplitCtrl')) {
- $control.append('<span class="arrowWidget" />');
- }
- var $popupControl = $control.closest('.PopupControl');
- if ($popupControl.length) {
- $control = $popupControl.addClass('PopupContainerControl');
- }
- $control.addClass('PopupControl');
- return $control;
- },
- /**
- * Opens or closes a menu, or navigates to another page, depending on menu status and control attributes.
- *
- * Clicking a control while the menu is hidden will open and show the menu.
- * If the control has an href attribute, clicking on it when the menu is open will navigate to the specified URL.
- * If the control does not have an href, a click will close the menu.
- *
- * @param event
- *
- * @return mixed
- */
- controlClick: function (e) {
- console.debug('%o control clicked. NewlyOpened: %s, Animated: %s', this.$control, this.newlyOpened, this.$menu.is(':animated'));
- if (!this.newlyOpened && !this.$menu.is(':animated')) {
- console.info('control: %o', this.$control);
- //if (this.$menu.is(':hidden')) {
- if (!this.$menu.hasClass('MenuOpened')) {
- this.showMenu(e, false);
- } else if (this.$clicker.attr('href') && !XenForo.isPositive(this.$clicker.data('closemenu'))) {
- console.warn('Following hyperlink from %o', this.$clicker);
- return true;
- } else {
- this.hideMenu(e, false);
- }
- } else {
- console.debug('Click on control of newly-opened or animating menu, ignored');
- }
- e.preventDefault();
- e.target.blur();
- return false;
- },
- /**
- * Handles hover events on menu controls. Will normally do nothing,
- * unless there is a menu open and the control being hovered belongs
- * to the same popupGroup, in which case this menu will open instantly.
- *
- * @param event
- *
- * @return mixed
- */
- controlHover: function (e) {
- if (this.popupGroup != null && this.popupGroup == this.getActiveGroup()) {
- this.showMenu(e, true);
- return false;
- }
- },
- /**
- * Handles hover-intent events on menu controls. Menu will show
- * if the cursor is hovered over a control at low speed and for a duration
- *
- * @param event
- */
- controlHoverIntent: function (e) {
- var instant = false;//(this.popupGroup != null && this.popupGroup == this.getActiveGroup());
- if (this.$clicker.hasClass('SplitCtrl')) {
- instant = true;
- }
- this.showMenu(e, instant);
- },
- /**
- * Opens and shows a popup menu.
- *
- * If the menu requires dynamic content to be loaded, this will load the content.
- * To define dynamic content, the .Menu element should have:
- * * data-contentSrc = URL to JSON that contains templateHtml to be inserted
- * * data-contentDest = jQuery selector specifying the element to which the templateHtml will be appended. Defaults to this.$menu.
- *
- * @param event
- * @param boolean Show instantly (true) or fade in (false)
- */
- showMenu: function (e, instant) {
- /*if (this.$menu.is(':visible')) {
- return false;
- }*/
- if (this.$menu.hasClass('MenuOpened') || this.$menu.closest(".xenOverlay").hasClass('faster')) {
- return false;
- }
- //console.log('Show menu event type = %s', e.type);
- var $eShow = new $.Event('PopupMenuShow');
- $eShow.$menu = this.$menu;
- $eShow.instant = instant;
- $(document).trigger($eShow);
- if ($eShow.isDefaultPrevented()) {
- return false;
- }
- this.menuVisible = true;
- this.setMenuPosition('showMenu');
- if (this.$menu.hasClass('BottomControl')) {
- instant = true;
- }
- if (this.contentSrc && !this.loading) {
- this.loading = XenForo.ajax(
- this.contentSrc, '',
- $.context(this, 'loadSuccess'),
- { type: 'GET' }
- );
- this.timeout = setTimeout(function () {
- this.$menu.find('.Progress').addClass('InProgress');
- }.bind(this), 500);
- instant = true;
- }
- this.setActiveGroup();
- this.$control.addClass('PopupOpen').removeClass('PopupClosed');
- this.$menu.addClass('MenuOpened');
- this.menuShown();
- if (!this.menuEventsInitialized) {
- var $html = $('html'),
- t = this,
- htmlSize = [$html.width(), $html.height()];
- // TODO: make this global?
- // TODO: touch interfaces don't like this
- $(document).bind({
- PopupMenuShow: $.context(this, 'hideIfOther'),
- XFOverlay: $.context(this, 'menuHidden')
- });
- if (XenForo.isTouchBrowser()) {
- for (const e of ['click', 'touchend'])
- document.addEventListener(e, e => {
- if (!this.menuVisible || !this.$menu.hasClass('MenuOpened')) return;
- if (!e.screenX && !e.screenY && e.type === 'click') return; // fix for $().click()
- if (!e.target.closest('nav') && !e.target.closest('.MenuOpened')) {
- e.preventDefault()
- e.stopImmediatePropagation()
- $(document).trigger('HideAllMenus')
- this.hideMenu($(e))
- }
- }, true)
- }
- // Webkit mobile kinda does not support document.click, bind to other elements
- if (XenForo._isWebkitMobile) {
- $(document.body.children).click($.context(this, 'hideMenu'));
- } else {
- $(document).click($.context(this, 'hideMenu'));
- }
- $(document).on('HideAllMenus', function (e) {
- if (t.menuVisible) {
- t._hideMenu(e, true);
- }
- });
- let hovered = true;
- t.$menu.hover(() => { hovered = true }, () => { hovered = false })
- if (!XenForo.isTouchBrowser()) {
- $(window).on('scroll', function (e) {
- if (t.menuVisible && !t.$menu.hasClass('HeaderMenu') && !hovered) {
- t._hideMenu(e, true);
- }
- });
- }
- $(window).bind(
- {
- resize: function (e) {
- // only trigger close if the window size actually changed - some mobile browsers trigger without size change
- var w = $html.width(), h = $html.height();
- if (w != htmlSize[0] || h != htmlSize[1]) {
- htmlSize[0] = w;
- htmlSize[1] = h;
- t._hideMenu(e);
- }
- }
- });
- this.$menu.delegate('a', 'click', $.context(this, 'menuLinkClick'));
- this.$menu.delegate('.MenuCloser', 'click', $.context(this, 'hideMenu'));
- this.$control.parents('#AlertsDestinationWrapper').on('scroll touchmove', $.context(this, 'hideMenu'));
- this.menuEventsInitialized = true;
- }
- },
- /**
- * Hides an open popup menu (conditionally)
- *
- * @param event
- * @param boolean Hide instantly (true) or fade out (false)
- */
- hideMenu: function (e, instant) {
- /*if (this.$menu.is(':visible') && this.triggersMenuHide(e)) {
- this._hideMenu(e, !instant);
- }*/
- this.$control.parents('#AlertsDestinationWrapper').off('scroll touchmove hover', $.context(this, 'hideMenu'));
- if (this.$menu.hasClass('MenuOpened') && this.triggersMenuHide(e)) {
- this._hideMenu(e, !instant);
- }
- },
- /**
- * Hides an open popup menu, without checking context or environment
- *
- * @param event
- * @param boolean Fade out the menu (true) or hide instantly out (false)
- */
- _hideMenu: function (e, fade) {
- //console.log('Hide menu \'%s\' %o TYPE = %s', this.$control.text(), this.$control, e.type);
- this.menuVisible = false;
- this.setActiveGroup(null);
- if (this.$menu.hasClass('BottomControl')) {
- fade = false;
- }
- // stop any unread content fading into its read state
- clearTimeout(this.unreadDisplayTimeout);
- this.$menu.find('.Unread').stop();
- /*var top = this.$menu.css('top');
- this.$menu.css('top', '+=10');
- this.$menu.xfHide(0, $.context(this, 'menuHidden')).css('top');*/
- this.menuHidden();
- },
- /**
- * Fires when the menu showing animation is completed and the menu is displayed
- */
- menuShown: function () {
- // if the menu has a data-contentSrc attribute, we can assume that it requires dynamic content, which has not yet loaded
- var contentLoaded = (this.$menu.data('contentsrc') ? false : true),
- $input = null;
- this.$control.addClass('PopupOpen').removeClass('PopupClosed');
- this.$menu.addClass('MenuOpened');
- this.newlyOpened = true;
- setTimeout($.context(function () {
- this.newlyOpened = false;
- }, this), 50);
- this.$menu.trigger('ShowComplete', [contentLoaded]);
- this.setMenuPosition('menuShown');
- this.highlightUnreadContent();
- if (!XenForo.isTouchBrowser()) {
- $input = this.$menu.find('input[type=text], input[type=search], textarea, select').first();
- if ($input.length) {
- if ($input.data('nofocus')) {
- return;
- }
- $input.select();
- }
- }
- const mm = $('#menu.mm--open')
- if (mm && mm.length) {
- $('#navigation .mobileMenuButtonClose').click()
- }
- },
- /**
- * Fires when the menu hiding animations is completed and the menu is hidden
- */
- menuHidden: function () {
- this.$control.removeClass('PopupOpen').addClass('PopupClosed');
- this.$menu.removeClass('MenuOpened');
- this.hideTimeout = setTimeout(() => {
- this.hideTimeout = null
- // https://zelenka.guru/threads/4163365/
- this.$menu.css('top', '0px').css('left', '0px')
- }, 150)
- this.$menu.trigger('MenuHidden');
- },
- /**
- * Fires in response to the document triggering 'PopupMenuShow' and hides the current menu
- * if the menu that fired the event is not itself.
- *
- * @param event
- */
- hideIfOther: function (e) {
- const eventMenuProp = e.$menu.prop($.expando);
- const contextMenuProp = this.$menu.prop($.expando);
- if (!eventMenuProp && !contextMenuProp || eventMenuProp != contextMenuProp) {
- if (!e.$menu.data('XenForo.PopupMenu').$container.hasClass('PopupInPopup')) {
- this.hideMenu(e, e.instant);
- }
- }
- },
- /**
- * Checks to see if an event should hide the menu.
- *
- * Returns false if:
- * * Event target is a child of the menu, or is the menu itself
- *
- * @param event
- *
- * @return boolean
- */
- triggersMenuHide: function (e) {
- var $target = $(e.target);
- if (e.ctrlKey || e.shiftKey || e.altKey) {
- return false;
- }
- if (e.which > 1) {
- // right or middle click, don't close
- return false;
- }
- if ($target.is('.MenuCloser')) {
- return true;
- }
- // is the control a hyperlink that has not had its default action prevented?
- if ($target.is('a[href]') && !e.isDefaultPrevented()) {
- return true;
- }
- if ($target.is('ul')) {
- return false;
- }
- if (e.target === document || !$target.closest('#' + this.$menu.id).length) {
- return true;
- }
- return false;
- },
- /**
- * Sets the position of the popup menu, based on the position of the control
- */
- setMenuPosition: function (caller) {
- if (this.hideTimeout) clearTimeout(this.hideTimeout)
- //console.info('setMenuPosition(%s)', caller);
- var $controlParent,
- controlLayout, // control coordinates
- menuLayout, // menu coordinates
- contentLayout, // #content coordinates
- $content,
- $window,
- proposedLeft,
- proposedTop;
- controlLayout = this.$control.coords('outer');
- this.$menu.css('position', '').removeData('position');
- $controlParent = this.$control;
- while ($controlParent && $controlParent.length && $controlParent.get(0) != document) {
- if ($controlParent.css('position') == 'fixed') {
- controlLayout.top -= $(window).scrollTop();
- controlLayout.left -= $(window).scrollLeft();
- this.$menu.css('position', 'fixed').data('position', 'fixed');
- break;
- }
- $controlParent = $controlParent.parent();
- }
- this.$control.removeClass('BottomControl');
- // set the menu to sit flush with the left of the control, immediately below it
- this.$menu.removeClass('BottomControl').css(
- {
- left: controlLayout.left,
- top: controlLayout.top + controlLayout.height - 1 // fixes a weird thing where the menu doesn't join the control
- });
- menuLayout = this.$menu.coords('outer');
- $content = $('#content .pageContent');
- if ($content.length) {
- contentLayout = $content.coords('outer');
- } else {
- contentLayout = $('body').coords('outer');
- }
- $window = $(window);
- var sT = $window.scrollTop(),
- sL = $window.scrollLeft(),
- windowWidth = $window.width();
- /*
- * if the menu's right edge is off the screen, check to see if
- * it would be better to position it flush with the right edge of the control.
- * RTL displays will try to do this if possible.
- */
- if (XenForo.isRTL() || menuLayout.left + menuLayout.width > contentLayout.left + contentLayout.width) {
- proposedLeft = Math.max(0, controlLayout.left + controlLayout.width - menuLayout.width);
- if (proposedLeft > sL) {
- this.$menu.css('left', proposedLeft);
- }
- }
- if (parseInt(this.$menu.css('left'), 10) + menuLayout.width > windowWidth + sL) {
- this.$menu.css('left', Math.min(parseInt(this.$menu.css('left'), 10), windowWidth + sL - menuLayout.width));
- }
- /*
- * if the menu's bottom edge is off the screen, check to see if
- * it would be better to position it above the control
- */
- if (menuLayout.top + menuLayout.height > $window.height() + sT) {
- proposedTop = controlLayout.top - menuLayout.height;
- if (proposedTop > sT) {
- this.$control.addClass('BottomControl');
- this.$menu.addClass('BottomControl');
- this.$menu.css('top', controlLayout.top - this.$menu.outerHeight());
- }
- }
- },
- /**
- * Fires when dynamic content for a popup menu has been loaded.
- *
- * Checks for errors and if there are none, appends the new HTML to the element selected by this.contentDest.
- *
- * @param object ajaxData
- * @param string textStatus
- */
- loadSuccess: function (ajaxData, textStatus) {
- if (XenForo.hasResponseError(ajaxData) || !XenForo.hasTemplateHtml(ajaxData)) {
- this._hideMenu()
- this.loading = null
- return false;
- }
- // check for content destination
- if (!this.contentDest) {
- console.warn('Menu content destination not specified, using this.$menu.');
- this.contentDest = this.$menu;
- }
- console.info('Content destination: %o', this.contentDest);
- var self = this;
- new XenForo.ExtLoader(ajaxData, function (data) {
- self.$menu.trigger('LoadComplete');
- if (self.timeout) clearTimeout(self.timeout);
- var $templateHtml = $(data.templateHtml);
- // append the loaded content to the destination
- $templateHtml.xfInsert(
- self.$menu.data('insertfn') || 'appendTo',
- self.contentDest,
- 'slideDown', 0,
- function () {
- self.$menu.css('min-width', '199px');
- setTimeout(function () {
- self.$menu.css('min-width', '');
- }, 0);
- if (self.$control.hasClass('PopupOpen')) {
- self.menuShown();
- }
- self.$menu.addClass('Loaded');
- }
- );
- if ($(self.contentDest).hasClass('Scrollbar')) {
- $(self.contentDest).scrollbar();
- }
- self.$menu.find('.Progress').removeClass('InProgress');
- if (self.useInfiniteScroll) {
- var $scroll = self.$menu.find('#AlertPanels #AlertsDestinationWrapper')
- var $status = $('\
- <div class="infinite-scroll-status">\
- <div class="spinner infinite-scroll-request" style="display: none;">\
- <div class="bounce1"></div>\
- <div class="bounce2"></div>\
- <div class="bounce3"></div>\
- </div>\
- </div>').appendTo($scroll)
- $scroll.find('.alertsPopup > ol').infiniteScroll({
- path: function () {
- return XenForo.canonicalizeUrl(self.contentSrc + (/\?/.test(self.contentSrc) ? '&' : '?') + 'page=' + (this.pageIndex + 1))
- },
- status: $status[0],
- append: '.alertsPopup > ol > li',
- xf: true,
- xfAjaxOptions: { global: false },
- xfAjaxDataProcess: function (ajaxData) {
- return ajaxData.templateHtml
- },
- history: false,
- elementScroll: $scroll[0]
- })
- }
- });
- },
- resetLoader: function () {
- if (this.contentDest && this.loading) {
- delete (this.loading);
- $(this.contentDest).empty();
- this.$menu.find('.Progress').addClass('InProgress');
- }
- },
- menuLinkClick: function (e) {
- this.hideMenu(e, true);
- },
- /**
- * Sets the name of the globally active popup group
- *
- * @param mixed If specified, active group will be set to this value.
- *
- * @return string Active group name
- */
- setActiveGroup: function (value) {
- var activeGroup = (value === undefined ? this.popupGroup : value);
- return XenForo._PopupMenuActiveGroup = activeGroup;
- },
- /**
- * Returns the name of the globally active popup group
- *
- * @return string Active group name
- */
- getActiveGroup: function () {
- return XenForo._PopupMenuActiveGroup;
- },
- /**
- * Fade return the background color of unread items to the normal background
- */
- highlightUnreadContent: function () {
- var $unreadContent = this.$menu.find('.Unread'),
- defaultBackground = null,
- counterSelector = null;
- if ($unreadContent.length) {
- defaultBackground = $unreadContent.data('defaultbackground');
- if (defaultBackground) {
- $unreadContent.css('backgroundColor', null);
- this.unreadDisplayTimeout = setTimeout($.context(function () {
- // removes an item specified by data-removeCounter on the menu element
- if (counterSelector = this.$menu.data('removecounter')) {
- XenForo.balloonCounterUpdate($(counterSelector), 0);
- }
- $unreadContent.animate({ backgroundColor: defaultBackground }, 2000, $.context(function () {
- $unreadContent.removeClass('Unread');
- this.$menu.trigger('UnreadDisplayComplete');
- }, this));
- }, this), 1000);
- }
- }
- },
- reload: function () {
- if (!this.contentSrc || (this.loading && this.loading.readyState !== 4)) return;
- this.resetLoader()
- this.loading = XenForo.ajax(
- this.contentSrc, '',
- $.context(this, 'loadSuccess'),
- { type: 'GET' }
- );
- },
- addToMenu: function ($element) {
- this.$menu.find('.secondaryContent').append($element).xfActivate();
- },
- removeFromMenu: function ($selector) {
- this.$menu.find($selector).remove();
- }
- };
- // *********************************************************************
- /**
- * Shows and hides global request pending progress indicators for AJAX calls.
- *
- * Binds to the global ajaxStart and ajaxStop jQuery events.
- * Also binds to the PseudoAjaxStart and PseudoAjaxStop events,
- * see XenForo.AutoInlineUploader
- *
- * Initialized by XenForo.init()
- */
- XenForo.AjaxProgress = function () {
- var overlay = null,
- showOverlay = function () {
- // mini indicators
- //$('.Progress, .xenForm .ctrlUnit.submitUnit dt').addClass('InProgress');
- // the overlay
- if (!overlay) {
- overlay = $('<div id="AjaxProgress" class="xenOverlay"><div class="content"><span class="close" /></div></div>')
- .appendTo('body')
- .css({
- top: 0,
- right: 0,
- opacity: 0,
- position: 'fixed',
- display: 'block',
- width: 'inherit'
- }).animate({ opacity: 1 }, XenForo.speed.fast)
- }
- },
- hideOverlay = function () {
- // mini indicators
- $('.Progress, .xenForm .ctrlUnit.submitUnit dt')
- .removeClass('InProgress');
- // the overlay
- if (overlay) {
- overlay.animate({ opacity: 0 }, XenForo.speed.fast, function () {
- $(this).remove()
- })
- overlay = null
- }
- };
- $(document).bind(
- {
- ajaxStart: function (e) {
- XenForo._AjaxProgress = true;
- showOverlay();
- },
- ajaxStop: function (e) {
- XenForo._AjaxProgress = false;
- hideOverlay();
- },
- PseudoAjaxStart: function (e) {
- showOverlay();
- },
- PseudoAjaxStop: function (e) {
- hideOverlay();
- }
- });
- if ($.browser.msie && $.browser.version < 7) {
- $(document).bind('scroll', function (e) {
- if (overlay && overlay.isOpened() && !overlay.getConf().fixed) {
- overlay.getOverlay().css('top', overlay.getConf().top + $(window).scrollTop());
- }
- });
- }
- };
- // *********************************************************************
- /**
- * Handles the scrollable pagenav gadget, allowing selection of any page between 1 and (end)
- * while showing only {range*2+1} pages plus first and last at once.
- *
- * @param jQuery .pageNav
- */
- XenForo.PageNav = function ($pageNav) {
- this.__construct($pageNav);
- };
- XenForo.PageNav.prototype =
- {
- __construct: function ($pageNav) {
- if (XenForo.isRTL()) {
- // scrollable doesn't support RTL yet
- return false;
- }
- this.$pageNav = $pageNav;
- var $scroller = $pageNav.find('.scrollable');
- if (!$scroller.length) {
- return false;
- }
- console.info('PageNav %o', $pageNav);
- this.start = parseInt($pageNav.data('start'));
- this.page = parseInt($pageNav.data('page'));
- this.end = parseInt($pageNav.data('end'));
- this.last = parseInt($pageNav.data('last'));
- this.range = parseInt($pageNav.data('range'));
- this.size = (this.range * 2 + 1);
- this.baseurl = $pageNav.data('baseurl');
- this.sentinel = $pageNav.data('sentinel');
- this.notShowPrevButton = false;
- this.notShowNextButton = false;
- $scroller.scrollable(
- {
- speed: XenForo.speed.slow,
- easing: 'easeOutBounce',
- keyboard: false,
- prev: '#nullPrev',
- next: '#nullNext',
- touch: false
- });
- this.api = $scroller.data('scrollable').onBeforeSeek($.context(this, 'beforeSeek'));
- this.$prevButton = $pageNav.find('.PageNavPrev').click($.context(this, 'prevPage'));
- this.$nextButton = $pageNav.find('.PageNavNext').click($.context(this, 'nextPage'));
- this.setControlVisibility(this.api.getIndex(), 0);
- },
- /**
- * Scrolls to the previous 'page' of page links, creating them if necessary
- *
- * @param Event e
- */
- prevPage: function (e) {
- if (this.api.getIndex() == 0 && this.start > 2) {
- var i = 0,
- minPage = Math.max(2, (this.start - this.size));
- for (i = this.start - 1; i >= minPage; i--) {
- this.prepend(i);
- }
- this.start = minPage;
- }
- this.api.seekTo(Math.max(this.api.getIndex() - this.size, 0));
- },
- /**
- * Scrolls to the next 'page' of page links, creating them if necessary
- *
- * @param Event e
- */
- nextPage: function (e) {
- if ((this.api.getIndex() + 1 + 2 * this.size) > this.api.getSize() && this.end < this.last - 1) {
- var i = 0,
- maxPage = Math.min(this.last - 1, this.end + this.size);
- for (i = this.end + 1; i <= maxPage; i++) {
- this.append(i);
- }
- this.end = maxPage;
- }
- this.api.seekTo(Math.min(this.api.getSize() - this.size, this.api.getIndex() + this.size));
- },
- /**
- * Adds an additional page link to the beginning of the scrollable section, out of sight
- *
- * @param integer page
- */
- prepend: function (page) {
- this.buildPageLink(page).prependTo(this.api.getItemWrap());
- this.api.next(0);
- },
- /**
- * Adds an additional page link to the end of the scrollable section, out of sight
- *
- * @param integer page
- */
- append: function (page) {
- this.buildPageLink(page).appendTo(this.api.getItemWrap());
- },
- /**
- * Buids a single page link
- *
- * @param integer page
- *
- * @return jQuery page link html
- */
- buildPageLink: function (page) {
- return $('<a />',
- {
- href: this.buildPageUrl(page),
- text: page,
- 'class': (page > 999 ? 'gt999' : '')
- });
- },
- /**
- * Converts the baseUrl into a page url by replacing the sentinel value
- *
- * @param integer page
- *
- * @return string page URL
- */
- buildPageUrl: function (page) {
- return this.baseurl
- .replace(this.sentinel, page)
- .replace(escape(this.sentinel), page);
- },
- /**
- * Runs immediately before the pagenav seeks to a new index,
- * Toggles visibility of the next/prev controls based on whether they are needed or not
- *
- * @param jQuery Event e
- * @param integer index
- */
- beforeSeek: function (e, index) {
- this.setControlVisibility(index, XenForo.speed.fast);
- this.$pageNav.trigger('seek');
- },
- /**
- * Sets the visibility of the scroll controls, based on whether using them would do anything
- * (hide the prev-page control if on the first page, etc.)
- *
- * @param integer Target index of the current scroll
- *
- * @param mixed Speed of animation
- */
- setControlVisibility: function (index, speed) {
- const notShowPrevButtonUpdated = index === 0 && this.start <= 2;
- const notShowNextButtonUpdated = this.api.getSize() - this.size <= index && this.end >= this.last - 1;
- if (notShowPrevButtonUpdated !== this.notShowPrevButton) {
- this.notShowPrevButton = notShowPrevButtonUpdated;
- if (this.notShowPrevButton) {
- this.$prevButton.hide(speed);
- } else {
- this.$prevButton.show(speed);
- }
- }
- if (notShowNextButtonUpdated !== this.notShowNextButton) {
- this.notShowNextButton = notShowNextButtonUpdated;
- if (this.notShowNextButton) {
- this.$nextButton.hide(speed);
- } else {
- this.$nextButton.show(speed);
- }
- }
- }
- };
- // *********************************************************************
- XenForo.ToggleTrigger = function ($trigger) {
- this.__construct($trigger);
- };
- XenForo.ToggleTrigger.prototype =
- {
- __construct: function ($trigger) {
- this.$trigger = $trigger;
- this.loaded = false;
- this.targetVisible = false;
- this.$target = null;
- if ($trigger.data('target')) {
- var anchor = $trigger.closest('.ToggleTriggerAnchor');
- if (!anchor.length) {
- anchor = $('body');
- }
- var target = anchor.find($trigger.data('target'));
- if (target.length) {
- this.$target = target;
- var toggleClass = target.data('toggle-class');
- this.targetVisible = toggleClass ? target.hasClass(toggleClass) : target.is(':visible');
- }
- }
- if ($trigger.data('only-if-hidden')
- && XenForo.isPositive($trigger.data('only-if-hidden'))
- && this.targetVisible
- ) {
- return;
- }
- $trigger.click($.context(this, 'toggle'));
- },
- toggle: function (e) {
- e.preventDefault();
- var $trigger = this.$trigger,
- $target = this.$target;
- if ($trigger.data('toggle-if-pointer') && XenForo.isPositive($trigger.data('toggle-if-pointer'))) {
- if ($trigger.css('cursor') !== 'pointer') {
- return;
- }
- }
- $trigger.clearQueue().finish();
- if ($trigger.data('toggle-text')) {
- var toggleText = $trigger.text();
- $trigger.text($trigger.data('toggle-text'));
- $trigger.data('toggle-text', toggleText);
- }
- if (e.pageX || e.pageY) {
- $trigger.blur();
- }
- if ($target) {
- $(document).trigger('ToggleTriggerEvent',
- {
- closing: this.targetVisible,
- $target: $target
- });
- this.hideSelfIfNeeded();
- var triggerTargetEvent = function () {
- $target.trigger('elementResized');
- };
- var hideTrigger = function () {
- $target.attr('style', 'display: block');
- triggerTargetEvent();
- }
- var toggleClass = $target.data('toggle-class');
- if (this.targetVisible) {
- if (toggleClass) {
- $target.removeClass(toggleClass);
- triggerTargetEvent();
- } else {
- $target.stop(true, true).xfFadeUp(null, triggerTargetEvent);
- }
- } else {
- if (toggleClass) {
- $target.addClass(toggleClass);
- hideTrigger();
- } else {
- $target.stop(true, true).xfFadeDown(null, hideTrigger);
- }
- }
- this.targetVisible = !this.targetVisible;
- } else {
- this.load();
- }
- },
- hideSelfIfNeeded: function () {
- var hideSel = this.$trigger.data('hide');
- if (!hideSel) {
- return false;
- }
- var $el;
- if (hideSel == 'self') {
- $el = this.$trigger;
- } else {
- var anchor = this.$trigger.closest('.ToggleTriggerAnchor');
- if (!anchor.length) {
- anchor = $('body');
- }
- $el = anchor.find(hideSel);
- }
- $el.hide();
- return;
- //$el.xfFadeUp();
- },
- load: function () {
- if (this.loading || !this.$trigger.attr('href')) {
- return;
- }
- var self = this;
- var $position = $(this.$trigger.data('position'));
- if (!$position.length) {
- $position = this.$trigger.closest('.ToggleTriggerAnchor');
- if (!$position.length) {
- console.warn("Could not match toggle target position selector %s", this.$trigger.data('position'));
- return false;
- }
- }
- var method = this.$trigger.data('position-method') || 'insertAfter';
- this.loading = true;
- XenForo.ajax(this.$trigger.attr('href'), {}, function (ajaxData) {
- self.loading = false;
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- // received a redirect rather than a view - follow it.
- if (ajaxData._redirectStatus && ajaxData._redirectTarget) {
- var fn = function () {
- XenForo.redirect(ajaxData._redirectTarget);
- };
- if (XenForo._manualDeferOverlay) {
- $(document).one('ManualDeferComplete', fn);
- } else {
- fn();
- }
- return false;
- }
- if (!ajaxData.templateHtml) {
- return false;
- }
- new XenForo.ExtLoader(ajaxData, function (data) {
- self.$target = $(data.templateHtml);
- self.$target.xfInsert(method, $position);
- self.targetVisible = true;
- self.hideSelfIfNeeded();
- });
- });
- }
- };
- // *********************************************************************
- /**
- * Triggers an overlay from a regular link or button
- * Triggers can provide an optional data-cacheOverlay attribute
- * to allow multiple trigers to access the same overlay.
- *
- * @param jQuery .OverlayTrigger
- */
- XenForo.OverlayTrigger = function ($trigger, options) {
- this.__construct($trigger, options);
- };
- XenForo.OverlayTrigger.prototype =
- {
- __construct: function ($trigger, options) {
- this.$trigger = $trigger.click($.context(this, 'show'));
- this.options = options;
- },
- /**
- * Begins the process of loading and showing an overlay
- *
- * @param event e
- */
- show: function (e) {
- //xfActivate
- var parentOverlay = this.$trigger.closest('.xenOverlay').data('overlay'),
- cache,
- options,
- isUserLink = (this.$trigger.is('.username, .avatar')),
- cardHref;
- if (!parseInt(XenForo._enableOverlays)) {
- // if no overlays, use <a href /> by preference
- if (this.$trigger.attr('href')) {
- return true;
- } else if (this.$trigger.data('href')) {
- if (this.$trigger.closest('.AttachmentUploader, #AttachmentUploader').length == 0) {
- // open the overlay target as a regular link, unless it's the attachment uploader
- XenForo.redirect(this.$trigger.data('href'));
- return false;
- }
- } else {
- // can't do anything - should not happen
- console.warn('No alternative action found for OverlayTrigger %o', this.$trigger);
- return true;
- }
- }
- // abort if this is a username / avatar overlay with NoOverlay specified
- if (isUserLink && this.$trigger.hasClass('NoOverlay')) {
- return true;
- }
- // abort if the event has a modifier key
- if (e.ctrlKey || e.shiftKey || e.altKey) {
- return true;
- }
- // abort if the event is a middle or right-button click
- if (e.which > 1) {
- return true;
- }
- if (this.options && this.options.onBeforeTrigger) {
- var newE = $.Event();
- newE.clickEvent = e;
- this.options.onBeforeTrigger(newE);
- if (newE.isDefaultPrevented()) {
- return;
- }
- }
- var beforeE = $.Event('BeforeOverlayTrigger');
- this.$trigger.trigger(beforeE);
- if (beforeE.isDefaultPrevented()) {
- return;
- }
- e.preventDefault();
- e.stopPropagation();
- e.stopImmediatePropagation();
- if (parentOverlay && parentOverlay.isOpened()) {
- var self = this;
- parentOverlay.getTrigger().one('onClose', function (innerE) {
- setTimeout(function () {
- self.show(innerE);
- }, 0);
- });
- parentOverlay.getConf().mask.closeSpeed = 0;
- parentOverlay.close();
- return;
- }
- $('#exposeMask').remove();
- if (!this.OverlayLoader) {
- options = (typeof this.options == 'object' ? this.options : {});
- options = $.extend(options, this.$trigger.data('overlayoptions'));
- cache = this.$trigger.data('cacheoverlay');
- if (cache !== undefined) {
- if (XenForo.isPositive(cache)) {
- cache = true;
- } else {
- cache = false;
- options.onClose = $.context(this, 'deCache');
- }
- } else if (this.$trigger.is('input:submit')) {
- cache = false;
- options.onClose = $.context(this, 'deCache');
- }
- if (isUserLink && !this.$trigger.hasClass('OverlayTrigger')) {
- if (!this.$trigger.data('cardurl') && this.$trigger.attr('href')) {
- cardHref = this.$trigger.attr('href').replace(/#.*$/, '');
- if (cardHref.indexOf('?') >= 0) {
- cardHref += '&card=1';
- } else {
- cardHref += '?card=1';
- }
- this.$trigger.data('cardurl', cardHref);
- }
- cache = true;
- options.speed = XenForo.speed.fast;
- }
- this.OverlayLoader = new XenForo.OverlayLoader(this.$trigger, cache, options);
- this.OverlayLoader.load();
- e.preventDefault();
- return true;
- }
- this.OverlayLoader.show();
- },
- deCache: function () {
- if (this.OverlayLoader && this.OverlayLoader.overlay) {
- console.info('DeCache %o', this.OverlayLoader.overlay.getOverlay());
- this.OverlayLoader.overlay.getTrigger().removeData('overlay');
- this.OverlayLoader.overlay.getOverlay().empty().remove();
- }
- delete (this.OverlayLoader);
- }
- };
- // *********************************************************************
- XenForo.LightBoxTrigger = function ($link) {
- $link.attr('data-fancybox', 'gallery');
- $link.on('click', function (e) {
- if (!XenForo.Fancybox) {
- XenForo.Fancybox = true;
- e.preventDefault();
- XenForo.scriptLoader.loadMultiple([{ 'type': 'js', 'url': 'js/fancybox/fancybox.umd.js' }, { 'type': 'css', 'list': ['fancybox'] }], function () {
- $link.children().click()
- })
- }
- })
- };
- // *********************************************************************
- XenForo.OverlayLoaderCache = {};
- /**
- * Loads HTML and related external resources for an overlay
- *
- * @param jQuery Overlay trigger object
- * @param boolean If true, cache the overlay HTML for this URL
- * @param object Object of options for the overlay
- */
- XenForo.OverlayLoader = function ($trigger, cache, options) {
- this.__construct($trigger, options, cache);
- };
- XenForo.OverlayLoader.prototype =
- {
- __construct: function ($trigger, options, cache) {
- this.$trigger = $trigger;
- this.cache = cache;
- this.options = options;
- },
- /**
- * Initiates the loading of the overlay, or returns it from cache
- *
- * @param function Callback to run on successful load
- */
- load: function (callback) {
- // special case for submit buttons
- if (this.$trigger.is('input:submit') || this.$trigger.is('button:submit')) {
- this.cache = false;
- if (!this.xhr) {
- var $form = this.$trigger.closest('form'),
- serialized = $form.serializeArray();
- serialized.push(
- {
- name: this.$trigger.attr('name'),
- value: this.$trigger.attr('value')
- });
- this.xhr = XenForo.ajax(
- $form.attr('action'),
- serialized,
- $.context(this, 'loadSuccess')
- );
- }
- return;
- }
- //TODO: ability to point to extant overlay HTML, rather than loading via AJAX
- this.href = this.$trigger.data('cardurl') || this.$trigger.data('href') || this.$trigger.attr('href');
- if (!this.href) {
- console.warn('No overlay href found for control %o', this.$trigger);
- return false;
- }
- console.info('OverlayLoader for %s', this.href);
- this.callback = callback;
- if (this.cache && XenForo.OverlayLoaderCache[this.href]) {
- XenForo.OverlayLoaderCache[this.href].load()
- } else if (!this.xhr) {
- this.xhr = XenForo.ajax(
- this.href, '',
- $.context(this, 'loadSuccess'), { type: 'GET' }
- );
- }
- },
- /**
- * Handles the returned ajaxdata from an overlay xhr load,
- * Stores the template HTML then inits externals (js, css) loading
- *
- * @param object ajaxData
- * @param string textStatus
- */
- loadSuccess: function (ajaxData, textStatus) {
- delete (this.xhr);
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- // received a redirect rather than a view - follow it.
- if (ajaxData._redirectStatus && ajaxData._redirectTarget) {
- var fn = function () {
- XenForo.redirect(ajaxData._redirectTarget);
- };
- if (XenForo._manualDeferOverlay) {
- $(document).one('ManualDeferComplete', fn);
- } else {
- fn();
- }
- return false;
- }
- this.options.title = ajaxData.h1 || ajaxData.title;
- new XenForo.ExtLoader(ajaxData, $.context(this, 'createOverlay'));
- },
- /**
- * Creates an overlay containing the appropriate template HTML,
- * runs the callback specified in .load() and then shows the overlay.
- *
- * @param jQuery Cached $overlay object
- */
- createOverlay: function ($overlay) {
- var contents = ($overlay && $overlay.templateHtml) ? $overlay.templateHtml : $overlay;
- this.overlay = XenForo.createOverlay(this.$trigger, contents, this.options);
- if (this.cache && this.overlay) {
- XenForo.OverlayLoaderCache[this.href] = this.overlay
- }
- if (typeof this.callback == 'function') {
- this.callback();
- }
- this.show();
- },
- /**
- * Shows a finished overlay
- */
- show: function () {
- if (!this.overlay && this.href === "misc/lightbox") {
- return;
- }
- if (!this.overlay) {
- console.warn('Attempted to call XenForo.OverlayLoader.show() for %s before overlay is created', this.href);
- this.load(this.callback);
- return;
- }
- this.overlay.load();
- $(document).trigger({
- type: 'XFOverlay',
- overlay: this.overlay,
- trigger: this.$trigger
- });
- }
- };
- // *********************************************************************
- XenForo.LoginBar = function ($loginBar) {
- var $form = $('#login').appendTo($loginBar.find('.pageContent')),
- /**
- * Opens the login form
- *
- * @param event
- */
- openForm = function (e) {
- e.preventDefault();
- XenForo.chromeAutoFillFix($form);
- $form.xfSlideIn(XenForo.speed.slow, 'easeOutBack', function () {
- $('#LoginControl').select();
- $loginBar.expose($.extend(XenForo._overlayConfig.mask,
- {
- loadSpeed: XenForo.speed.slow,
- onBeforeLoad: function (e) {
- $form.css('outline', '0px solid black');
- },
- onLoad: function (e) {
- $form.css('outline', '');
- },
- onBeforeClose: function (e) {
- closeForm(false, true);
- return true;
- }
- }));
- });
- },
- /**
- * Closes the login form
- *
- * @param event
- * @param boolean
- */
- closeForm = function (e, isMaskClosing) {
- if (e) e.target.blur();
- $form.xfSlideOut(XenForo.speed.fast);
- if (!isMaskClosing && $.mask) {
- $.mask.close();
- }
- };
- /**
- * Toggles the login form
- */
- $('label[for="LoginControl"]').click(function (e) {
- if ($(this).closest('#login').length == 0) {
- e.preventDefault();
- if ($form._xfSlideWrapper(true)) {
- closeForm(e);
- } else {
- $(XenForo.getPageScrollTagName()).scrollTop(0);
- openForm(e);
- }
- }
- });
- /**
- * Changes the text of the Log in / Sign up submit button depending on state
- */
- $loginBar.delegate('input[name="register"]', 'click', function (e) {
- var $button = $form.find('input.button.primary'),
- register = $form.find('input[name="register"]:checked').val();
- $form.find('input.button.primary').val(register == '1'
- ? $button.data('signupphrase')
- : $button.data('loginphrase'));
- $form.find('label.rememberPassword').css('visibility', (register == '1' ? 'hidden' : 'visible'));
- });
- // close form if any .click elements within it are clicked
- $loginBar.delegate('.close', 'click', closeForm);
- };
- // *********************************************************************
- XenForo.QuickSearch = function ($target) { this.__construct($target); };
- XenForo.QuickSearch.prototype = {
- $target: null,
- $form: null,
- $queryInput: null,
- $clearButton: null,
- $searchResults: null,
- updateTimer: null,
- xhr: null,
- runCount: 0,
- __construct: function ($target) {
- this.$target = $target;
- const $form = this.$form = $target.find('form');
- this.$queryInput = $form.find('.QuickSearchQuery')
- .on('focus', $.context(this, 'focus'))
- .on('input', $.context(this, 'input'));
- this.$clearButton = $form.find('.QuickSearchClear')
- .on('click', $.context(this, 'clearQueryInput'));
- this.$searchResults = $('.SearchResultsList');
- $('#QuickSearchPlaceholder').on('click', $.context(this, 'placeholderClick'));
- },
- placeholderClick: function (e) {
- e.preventDefault();
- setTimeout(function () {
- $('#QuickSearch').addClass('show');
- $('#QuickSearchPlaceholder').addClass('hide');
- $('#QuickSearchQuery').focus();
- if (XenForo.isTouchBrowser()) {
- $('#QuickSearchQuery').blur();
- }
- });
- },
- focus: function (e) {
- const $target = this.$target,
- runCount = ++this.runCount;
- console.log('Show quick search menu (%d)', runCount);
- if (runCount === 1) {
- if ($.browser.msie && $.browser.version < 9) {
- const $form = this.$form;
- $form.find('input').on('keydown', function (e) {
- if (e.keyCode === 13) {
- if (e.which === 13) {
- clearTimeout(this.updateTimer);
- }
- $form.submit();
- return false;
- }
- });
- }
- $(XenForo._isWebkitMobile ? document.body.children : document).on(
- 'click',
- function (clickEvent) {
- if (!$(clickEvent.target).closest('.QuickSearch').length) {
- console.log('Hide quick search menu');
- $('#QuickSearch').removeClass('show');
- $('#QuickSearchPlaceholder').removeClass('hide');
- $target.find('.secondaryControls').slideUp(XenForo.speed.xfast, function () {
- $target.removeClass('active');
- if ($.browser.msie) {
- $('body').css('zoom', 1);
- setTimeout(function () {
- $('body').css('zoom', '');
- }, 100);
- }
- });
- }
- }
- );
- }
- $target.addClass('active');
- $target.find('.secondaryControls').slideDown(0);
- },
- input: function (e) {
- if ($(e.currentTarget).val()) {
- this.$target.addClass('hasInputValue');
- this.$clearButton.addClass('show')
- }
- else {
- this.$target.removeClass('hasInputValue');
- this.$clearButton.removeClass('show')
- }
- if (this.updateTimer) {
- clearTimeout(this.updateTimer);
- }
- const self = this;
- this.updateTimer = setTimeout(function () {
- if (self.$queryInput.val().length < 3) {
- self.$searchResults.html('');
- return;
- }
- this.xhr = XenForo.ajax(
- self.$target.data('member-search-url'),
- {
- username: self.$queryInput.val()
- },
- function (data, status) {
- if (XenForo.hasResponseError(data, status)) {
- return false;
- }
- else {
- self.$searchResults.html('');
- $(data.templateHtml).xfInsert('appendTo', self.$searchResults);
- }
- }
- );
- }, 500);
- },
- clearQueryInput: function () {
- this.$queryInput.val('').trigger('input');
- this.$searchResults.html('');
- if (this.xhr) {
- this.xhr.abort();
- this.xhr = null;
- }
- }
- };
- // *********************************************************************
- XenForo.configureTooltipRtl = function (config) {
- if (config.offset !== undefined) {
- config.offset = XenForo.switchOffsetRTL(config.offset);
- }
- if (config.position !== undefined) {
- config.position = XenForo.switchStringRTL(config.position);
- }
- return config;
- };
- /**
- * Wrapper for jQuery Tools Tooltip
- *
- * @param $element
- */
- XenForo.Tooltip = function ($element) {
- if ($element.hasClass('PopupTooltip')) {
- var isFirstShown = true;
- var isElementLoaded = false;
- var next = $element.next($($element.data('content')))
- var $content = $('<div>').append(next.clone()).html();
- if (!next.hasClass('control')) {
- next.remove()
- }
- const like_link = $element.data('likes-url');
- if (like_link) {
- tippy($element.get(), {
- content: "",
- theme: 'popup',
- interactive: true,
- arrow: true,
- maxWidth: $element.data('width') || 250,
- animation: 'shift-toward',
- hideOnClick: XenForo.isTouchBrowser(),
- placement: $element.data('placement') || 'top',
- zIndex: $element.closest('.xenOverlay').length ? 11111 : 9000, // overlay fix
- trigger: XenForo.isTouchBrowser() ? 'click' : 'mouseenter focus', // Touch fix
- onTrigger: function () {
- if (!isElementLoaded || !$element.get(0)._tippy.props.content) {
- setTimeout(() => {
- $element.get(0)._tippy.hide();
- }, 0)
- XenForo.ajax(like_link, {}, function (ajaxData) {
- if (ajaxData.templateHtml) {
- isElementLoaded = true;
- $element.get(0)._tippy.setContent(ajaxData.templateHtml);
- if ($element.is(':hover')) {
- $element.get(0)._tippy.show();
- }
- }
- }.bind(this));
- }
- },
- onShown: function () {
- if ($('body').hasClass('iOS')) {
- $element.click();
- }
- if (!isFirstShown) return;
- $('.' + $(this.content).attr('class')).xfActivate();
- isFirstShown = false;
- }
- });
- } else {
- tippy($element.get(), {
- content: $content,
- theme: 'popup',
- interactive: true,
- arrow: true,
- maxWidth: $element.data('width') || 250,
- animation: 'shift-toward',
- hideOnClick: XenForo.isTouchBrowser(),
- placement: $element.data('placement') || 'top',
- zIndex: $element.closest('.xenOverlay').length ? 11111 : 9000, // overlay fix
- trigger: XenForo.isTouchBrowser() ? 'click' : 'mouseenter focus', // Touch fix
- onShown: function () {
- if ($('body').hasClass('iOS')) {
- $element.click();
- }
- if (!isFirstShown) return;
- $('.' + $(this.content).attr('class')).xfActivate();
- isFirstShown = false;
- }
- });
- }
- } else {
- var title = $element.attr('title');
- let cachedtitle = $element.attr('data-cachedtitle')
- if (!title && cachedtitle) {
- title = cachedtitle // if restored from cache (title is empty)
- }
- $element.attr('data-cachedtitle', title)
- $element.attr('title', '');
- tippy($element.get(), {
- arrow: true,
- animation: 'shift-toward',
- distance: 5,
- maxWidth: 250,
- zIndex: 11111,
- placement: $element.data('placement') || 'top',
- content: XenForo.htmlspecialchars(title),
- trigger: XenForo.isTouchBrowser() ? 'click' : 'mouseenter focus', // Touch fix
- onShow: function () {
- if ($('body').hasClass('iOS')) {
- $element.click();
- }
- },
- });
- }
- };
- // *********************************************************************
- /*XenForo.StatusTooltip = function ($element) {
- if ($element.attr('title')) {
- var title = XenForo.htmlspecialchars($element.attr('title'));
- $element.attr('title', title).tooltip(XenForo.configureTooltipRtl(
- {
- effect: 'slide',
- slideOffset: 30,
- position: 'bottom left',
- offset: [10, 1],
- tipClass: 'xenTooltip statusTip',
- layout: '<div><span class="arrow" /></div>'
- }));
- }
- };*/
- // *********************************************************************
- /*XenForo.NodeDescriptionTooltip = function ($title) {
- var description = $title.data('description');
- if (description && $(description).length) {
- var $description = $(description)
- .addClass('xenTooltip nodeDescriptionTip')
- .appendTo('body')
- .append('<span class="arrow" />');
- $title.tooltip(XenForo.configureTooltipRtl(
- {
- effect: 'slide',
- slideOffset: 30,
- offset: [30, 10],
- slideInSpeed: XenForo.speed.xfast,
- slideOutSpeed: 50 * XenForo._animationSpeedMultiplier,
- /!*effect: 'fade',
- fadeInSpeed: XenForo.speed.xfast,
- fadeOutSpeed: XenForo.speed.xfast,*!/
- predelay: 250,
- position: 'bottom right',
- tip: description,
- onBeforeShow: function () {
- if (!$title.data('tooltip-shown')) {
- if ($(window).width() < 600) {
- var conf = $title.data('tooltip').getConf();
- conf.slideOffset = 0;
- conf.effect = 'toggle';
- conf.offset = [20, -$title.width()];
- conf.position = ['top', 'right'];
- if (XenForo.isRTL()) {
- conf.offset[1] *= -1;
- conf.position[1] = 'left';
- }
- $description.addClass('arrowBottom');
- }
- $title.data('tooltip-shown', true);
- }
- }
- }));
- $title.click(function () {
- $(this).data('tooltip').hide();
- });
- }
- };*/
- // *********************************************************************
- XenForo.AccountMenu = function ($menu) {
- $menu.find('.submitUnit').hide();
- $menu.find('.StatusEditor').focus(function (e) {
- if ($menu.is(':visible')) {
- $menu.find('.submitUnit').show();
- }
- });
- };
- // *********************************************************************
- XenForo.FollowLink = function ($link) {
- $link.click(function (e) {
- e.preventDefault();
- $link.get(0).blur();
- XenForo.ajax(
- $link.attr('href'),
- { _xfConfirm: 1 },
- function (ajaxData, textStatus) {
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- $link.xfFadeOut(XenForo.speed.fast, function () {
- $link
- .attr('href', ajaxData.linkUrl)
- .html(ajaxData.linkPhrase)
- .xfFadeIn(XenForo.speed.fast);
- });
- }
- );
- });
- };
- // *********************************************************************
- /**
- * Allows relative hash links to smoothly scroll into place,
- * Primarily used for 'x posted...' messages on bb code quote.
- *
- * @param jQuery a.AttributionLink
- */
- XenForo.AttributionLink = function ($link) {
- $link.click(function (e) {
- if ($(this.hash).length) {
- try {
- var hash = this.hash,
- top = $(this.hash).offset().top,
- scroller = XenForo.getPageScrollTagName();
- if ("pushState" in window.history) {
- window.history.pushState({}, '', window.location.toString().replace(/#.*$/, '') + hash);
- }
- var menu_height = $('#navigation').get(0).getBoundingClientRect().height || 45;
- $(scroller).animate({ scrollTop: top - menu_height }, XenForo.speed.normal, 'easeOutBack', function () {
- if (!window.history.pushState) {
- window.location.hash = hash;
- }
- XenForo.animateBackgroundColor($(hash));
- });
- } catch (e) {
- window.location.hash = this.hash;
- }
- e.preventDefault();
- }
- });
- };
- // *********************************************************************
- /**
- * Allows clicks on one element to trigger the click event of another
- *
- * @param jQuery .ClickProxy[rel="{selectorForTarget}"]
- *
- * @return boolean false - prevents any direct action for the proxy element on click
- */
- XenForo.ClickProxy = function ($element) {
- $element.click(function (e) {
- $($element.attr('rel')).click();
- if (!$element.data('allowdefault')) {
- return false;
- }
- });
- };
- // *********************************************************************
- /**
- * ReCaptcha wrapper
- */
- XenForo.ReCaptcha = function ($captcha) {
- this.__construct($captcha);
- };
- XenForo.ReCaptcha.prototype =
- {
- __construct: function ($captcha) {
- if (XenForo.ReCaptcha.instance) {
- XenForo.ReCaptcha.instance.remove();
- }
- XenForo.ReCaptcha.instance = this;
- this.publicKey = $captcha.data('publickey');
- if (!this.publicKey) {
- return;
- }
- $captcha.siblings('noscript').remove();
- $captcha.uniqueId();
- this.$captcha = $captcha;
- this.type = 'image';
- $captcha.find('.ReCaptchaReload').click($.context(this, 'reload'));
- $captcha.find('.ReCaptchaSwitch').click($.context(this, 'switchType'));
- this.load();
- $captcha.closest('form.AutoValidator').bind(
- {
- AutoValidationDataReceived: $.context(this, 'reload')
- });
- },
- load: function () {
- if (window.Recaptcha) {
- this.create();
- } else {
- var f = $.context(this, 'create'),
- delay = ($.browser.msie && $.browser.version <= 6 ? 250 : 0); // helps IE6 loading
- $.getScript('https://www.google.com/recaptcha/api/js/recaptcha_ajax.js',
- function () {
- setTimeout(f, delay);
- }
- );
- }
- },
- create: function () {
- var $c = this.$captcha;
- window.Recaptcha.create(this.publicKey, $c.attr('id'),
- {
- theme: 'custom',
- callback: function () {
- $c.show();
- $('#ReCaptchaLoading').remove();
- // webkit seems to overwrite this value using the back button
- $('#recaptcha_challenge_field').val(window.Recaptcha.get_challenge());
- }
- });
- },
- reload: function (e) {
- if (!window.Recaptcha) {
- return;
- }
- if (!$(e.target).is('form')) {
- e.preventDefault();
- }
- window.Recaptcha.reload();
- },
- switchType: function (e) {
- e.preventDefault();
- this.type = (this.type == 'image' ? 'audio' : 'image');
- window.Recaptcha.switch_type(this.type);
- },
- remove: function () {
- this.$captcha.empty().remove();
- if (window.Recaptcha) {
- window.Recaptcha.destroy();
- }
- }
- };
- XenForo.ReCaptcha.instance = null;
- XenForo.NoCaptcha = function ($captcha) {
- this.__construct($captcha);
- };
- XenForo.NoCaptcha.prototype =
- {
- __construct: function ($captcha) {
- this.$captcha = $captcha;
- this.noCaptchaId = null;
- $captcha.closest('form.AutoValidator').bind(
- {
- AutoValidationDataReceived: $.context(this, 'reload')
- });
- if (window.grecaptcha) {
- this.create();
- } else {
- XenForo.NoCaptcha._callbacks.push($.context(this, 'create'));
- $.getScript('https://www.google.com/recaptcha/api.js?onload=XFNoCaptchaCallback&render=explicit');
- }
- },
- create: function () {
- if (!window.grecaptcha) {
- return;
- }
- this.noCaptchaId = grecaptcha.render(this.$captcha[0], { sitekey: this.$captcha.data('sitekey') });
- },
- reload: function () {
- if (!window.grecaptcha || this.noCaptchaId === null) {
- return;
- }
- grecaptcha.reset(this.noCaptchaId);
- }
- };
- XenForo.NoCaptcha._callbacks = [];
- window.XFNoCaptchaCallback = function () {
- var cb = XenForo.NoCaptcha._callbacks;
- for (var i = 0; i < cb.length; i++) {
- cb[i]();
- }
- };
- // *********************************************************************
- XenForo.SolveMediaCaptcha = function ($captcha) {
- this.__construct($captcha);
- };
- XenForo.SolveMediaCaptcha.prototype =
- {
- __construct: function ($captcha) {
- if (XenForo.SolveMediaCaptcha.instance) {
- XenForo.SolveMediaCaptcha.instance.remove();
- }
- XenForo.SolveMediaCaptcha.instance = this;
- this.cKey = $captcha.data('c-key');
- if (!this.cKey) {
- return;
- }
- $captcha.siblings('noscript').remove();
- $captcha.uniqueId();
- this.$captcha = $captcha;
- this.type = 'image';
- this.load();
- $captcha.closest('form.AutoValidator').bind(
- {
- AutoValidationDataReceived: $.context(this, 'reload')
- });
- },
- load: function () {
- if (window.ACPuzzle) {
- this.create();
- } else {
- var prefix = window.location.protocol == 'https:' ? 'https://api-secure' : 'http://api';
- window.ACPuzzleOptions = {
- onload: $.context(this, 'create')
- };
- XenForo.loadJs(prefix + '.solvemedia.com/papi/challenge.ajax');
- }
- },
- create: function () {
- var $c = this.$captcha;
- window.ACPuzzle.create(this.cKey, $c.attr('id'), {
- theme: $c.data('theme') || 'white',
- lang: $('html').attr('lang').substr(0, 2) || 'en'
- });
- },
- reload: function (e) {
- if (!window.ACPuzzle) {
- return;
- }
- if (!$(e.target).is('form')) {
- e.preventDefault();
- }
- window.ACPuzzle.reload();
- },
- remove: function () {
- this.$captcha.empty().remove();
- if (window.ACPuzzle) {
- window.ACPuzzle.destroy();
- }
- }
- };
- XenForo.SolveMediaCaptcha.instance = null;
- // *********************************************************************
- XenForo.KeyCaptcha = function ($captcha) {
- this.__construct($captcha);
- };
- XenForo.KeyCaptcha.prototype =
- {
- __construct: function ($captcha) {
- this.$captcha = $captcha;
- this.$form = $captcha.closest('form');
- this.$form.uniqueId();
- this.$codeEl = this.$form.find('input[name=keycaptcha_code]');
- this.$codeEl.uniqueId();
- this.load();
- $captcha.closest('form.AutoValidator').bind({
- AutoValidationDataReceived: $.context(this, 'reload')
- });
- },
- load: function () {
- if (window.s_s_c_onload) {
- this.create();
- } else {
- var $captcha = this.$captcha;
- window.s_s_c_user_id = $captcha.data('user-id');
- window.s_s_c_session_id = $captcha.data('session-id');
- window.s_s_c_captcha_field_id = this.$codeEl.attr('id');
- window.s_s_c_submit_button_id = 'sbutton-#-r';
- window.s_s_c_web_server_sign = $captcha.data('sign');
- window.s_s_c_web_server_sign2 = $captcha.data('sign2');
- document.s_s_c_element = this.$form[0];
- document.s_s_c_debugmode = 1;
- var $div = $('#div_for_keycaptcha');
- if (!$div.length) {
- $('body').append('<div id="div_for_keycaptcha" />');
- }
- XenForo.loadJs('https://backs.keycaptcha.com/swfs/cap.js');
- }
- },
- create: function () {
- window.s_s_c_onload(this.$form.attr('id'), this.$codeEl.attr('id'), 'sbutton-#-r');
- },
- reload: function (e) {
- if (!window.s_s_c_onload) {
- return;
- }
- if (!$(e.target).is('form')) {
- e.preventDefault();
- }
- this.load();
- }
- };
- // *********************************************************************
- /**
- * Loads a new (non-ReCaptcha) CAPTCHA upon verification failure
- *
- * @param jQuery #Captcha
- */
- XenForo.Captcha = function ($container) {
- var $form = $container.closest('form');
- $form.off('AutoValidationDataReceived.captcha').on('AutoValidationDataReceived.captcha', function (e) {
- $container.fadeTo(XenForo.speed.fast, 0.5);
- XenForo.ajax($container.data('source'), {}, function (ajaxData, textStatus) {
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- if (XenForo.hasTemplateHtml(ajaxData)) {
- $container.xfFadeOut(XenForo.speed.xfast, function () {
- $(ajaxData.templateHtml).xfInsert('replaceAll', $container, 'xfFadeIn', XenForo.speed.xfast);
- });
- }
- });
- });
- };
- // *********************************************************************
- /**
- * Handles resizing of BB code [img] tags that would overflow the page
- *
- * @param jQuery img.bbCodeImage
- */
- XenForo.BbCodeImage = function ($image) {
- this.__construct($image);
- };
- XenForo.BbCodeImage.prototype =
- {
- __construct: function ($image) {
- this.$image = $image;
- this.actualWidth = 0;
- if ($image.closest('a').length) {
- return;
- }
- $image
- .attr('title', XenForo.phrases.click_image_show_full_size_version || 'Show full size')
- .click($.context(this, 'toggleFullSize'));
- if (!XenForo.isTouchBrowser()) {
- this.$image.tooltip(XenForo.configureTooltipRtl({
- effect: 'slide',
- slideOffset: 30,
- position: 'top center',
- offset: [45, 0],
- tipClass: 'xenTooltip bbCodeImageTip',
- onBeforeShow: $.context(this, 'isResized'),
- onShow: $.context(this, 'addTipClick')
- }));
- }
- if (!this.getImageWidth()) {
- var src = $image.attr('src');
- $image.bind({
- load: $.context(this, 'getImageWidth')
- });
- //$image.attr('src', 'about:blank');
- $image.attr('src', src);
- }
- },
- /**
- * Attempts to store the un-resized width of the image
- *
- * @return integer
- */
- getImageWidth: function () {
- this.$image.css({ 'max-width': 'none', 'max-height': 'none' });
- this.actualWidth = this.$image.width();
- this.$image.css({ 'max-width': '', 'max-height': '' });
- //console.log('BB Code Image %o has width %s', this.$image, this.actualWidth);
- return this.actualWidth;
- },
- /**
- * Shows and hides a full-size version of the image
- *
- * @param event
- */
- toggleFullSize: function (e) {
- if (this.actualWidth == 0) {
- this.getImageWidth();
- }
- var currentWidth = this.$image.width(),
- offset, cssOffset, scale,
- scrollLeft, scrollTop,
- layerX, layerY,
- $fullSizeImage,
- speed = window.navigator.userAgent.match(/Android|iOS|iPhone|iPad|Mobile Safari/i) ? 0 : XenForo.speed.normal,
- easing = 'easeInOutQuart';
- if (this.actualWidth > currentWidth) {
- offset = this.$image.offset();
- cssOffset = offset;
- scale = this.actualWidth / currentWidth;
- layerX = e.pageX - offset.left;
- layerY = e.pageY - offset.top;
- if (XenForo.isRTL()) {
- cssOffset.right = $('html').width() - cssOffset.left - currentWidth;
- cssOffset.left = 'auto';
- }
- $fullSizeImage = $('<img />', { src: this.$image.attr('src') })
- .addClass('bbCodeImageFullSize')
- .css('width', currentWidth)
- .css(cssOffset)
- .click(function () {
- $(this).remove();
- $(XenForo.getPageScrollTagName()).scrollLeft(0).scrollTop(offset.top);
- })
- .appendTo('body')
- .animate({ width: this.actualWidth }, speed, easing);
- // remove full size image if an overlay is about to open
- $(document).one('OverlayOpening', function () {
- $fullSizeImage.remove();
- });
- // remove full-size image if the source image is contained by a ToggleTrigger target that is closing
- $(document).bind('ToggleTriggerEvent', $.context(function (e, args) {
- if (args.closing && args.$target.find(this.$image).length) {
- console.info('Target is parent of this image %o', this.$image);
- $fullSizeImage.remove();
- }
- }, this));
- if (e.target == this.$image.get(0)) {
- scrollLeft = offset.left + (e.pageX - offset.left) * scale - $(window).width() / 2;
- scrollTop = offset.top + (e.pageY - offset.top) * scale - $(window).height() / 2;
- } else {
- scrollLeft = offset.left + (this.actualWidth / 2) - $(window).width() / 2;
- scrollTop = offset.top + (this.$image.height() * scale / 2) - $(window).height() / 2;
- }
- $(XenForo.getPageScrollTagName()).animate(
- {
- scrollLeft: scrollLeft,
- scrollTop: scrollTop
- }, speed, easing, $.context(function () {
- var tooltip = this.$image.data('tooltip');
- if (tooltip) {
- tooltip.hide();
- }
- }, this));
- } else {
- console.log('BBCodeImage: this.actualWidth = %d, currentWidth = %d', this.actualWidth, currentWidth);
- }
- },
- isResized: function (e) {
- var width = this.$image.width();
- if (!width) {
- return false;
- }
- if (this.getImageWidth() <= width) {
- //console.log('Image is not resized %o', this.$image);
- return false;
- }
- },
- addTipClick: function (e) {
- if (!this.tipClickAdded) {
- $(this.$image.data('tooltip').getTip()).click($.context(this, 'toggleFullSize'));
- this.tipClickAdded = true;
- }
- }
- };
- // *********************************************************************
- /**
- * Wrapper for the jQuery Tools Tabs system
- *
- * @param jQuery .Tabs
- */
- XenForo.Tabs = function ($tabContainer) {
- this.__construct($tabContainer);
- };
- XenForo.Tabs.prototype =
- {
- __construct: function ($tabContainer) {
- // var useHistory = XenForo.isPositive($tabContainer.data('history'));
- // TODO: disabled until base tag issues are resolved
- var useHistory = false;
- this.$tabContainer = $tabContainer;
- this.$panes = $($tabContainer.data('panes'));
- /*if (useHistory)
- {
- $tabContainer.find('a[href]').each(function()
- {
- var $this = $(this), hrefParts = $this.attr('href').split('#');
- if (hrefParts[1] && location.pathname == hrefParts[0])
- {
- $this.attr('href', '#' + hrefParts[1]);
- }
- });
- }*/
- var $tabs = $tabContainer.find('a');
- if (!$tabs.length) {
- $tabs = $tabContainer.children();
- }
- var $active = $tabs.filter('.active'),
- initialIndex = 0;
- if ($active.length) {
- $tabs.each(function () {
- if (this == $active.get(0)) {
- return false;
- }
- initialIndex++;
- });
- }
- if (window.location.hash.length > 1) {
- var id = window.location.hash.substr(1),
- matchIndex = -1,
- matched = false;
- this.$panes.each(function () {
- matchIndex++;
- if ($(this).attr('id') === id) {
- matched = true;
- return false;
- }
- return true;
- });
- if (matched) {
- initialIndex = matchIndex;
- }
- }
- $tabContainer.tabs(this.$panes, {
- current: 'active',
- history: useHistory,
- initialIndex: initialIndex,
- onBeforeClick: $.context(this, 'onBeforeClick')
- });
- this.api = $tabContainer.data('tabs');
- this.useInfiniteScroll = !!this.$panes.parents('#AlertPanels').length
- },
- getCurrentTab: function () {
- return this.api.getIndex();
- },
- click: function (index) {
- this.api.click(index);
- },
- onBeforeClick: function (e, index) {
- var self = this
- this.$tabContainer.children().each(function (i) {
- if (index == i) {
- $(this).addClass('active');
- } else {
- $(this).removeClass('active');
- }
- });
- var $pane = $(this.$panes.get(index)),
- loadUrl = $pane.data('loadurl');
- if (loadUrl) {
- $pane.data('loadurl', '');
- XenForo.ajax(loadUrl, {}, function (ajaxData) {
- if (XenForo.hasTemplateHtml(ajaxData) || XenForo.hasTemplateHtml(ajaxData, 'message')) {
- new XenForo.ExtLoader(ajaxData, function (ajaxData) {
- var $html;
- if (ajaxData.templateHtml) {
- $html = $(ajaxData.templateHtml);
- } else if (ajaxData.message) {
- $html = $('<div class="section" />').html(ajaxData.message);
- }
- $pane.html('');
- if ($html) {
- $html.xfInsert('appendTo', $pane, 'xfFadeIn', 0);
- }
- if ($pane.hasClass('Scrollbar')) {
- $pane.scrollbar();
- }
- if ($html.length > 1) {
- $pane.parent().xfActivate()
- }
- if ($pane.find('form.InlineModForm').length) {
- XenForo.create('XenForo.InlineModForm', $pane.find('form.InlineModForm'));
- }
- if (self.useInfiniteScroll) {
- var $status = $('<div class="infinite-scroll-status">\
- <div class="spinner infinite-scroll-request" style="display: none;">\
- <div class="bounce1"></div>\
- <div class="bounce2"></div>\
- <div class="bounce3"></div>\
- </div>\
- </div>').appendTo($pane)
- $pane.find('.alertsPopup > ol').infiniteScroll({
- path: function () {
- return XenForo.canonicalizeUrl(loadUrl + (/\?/.test(loadUrl) ? '&' : '?') + 'page=' + (this.pageIndex + 1))
- },
- status: $status[0],
- append: '.alertsPopup > ol > li',
- xf: true,
- xfAjaxOptions: { global: false },
- xfAjaxDataProcess: function (ajaxData) {
- return ajaxData.templateHtml
- },
- history: false,
- elementScroll: $pane[0]
- })
- }
- });
- } else if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- }, { type: 'GET' });
- }
- }
- };
- // *********************************************************************
- /**
- * Handles a like / unlike link being clicked
- *
- * @param jQuery a.LikeLink
- */
- XenForo.LikeLink = function ($link) {
- $link.click(function (e) {
- e.preventDefault();
- var $link = $(this);
- XenForo.ajax(this.href, {}, function (ajaxData, textStatus) {
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- $link.stop(true, true);
- if (ajaxData.term) // term = Like / Unlike
- {
- $link.find('.LikeLabel').html(ajaxData.term);
- if (ajaxData.cssClasses) {
- $.each(ajaxData.cssClasses, function (className, action) {
- $link[action === '+' ? 'addClass' : 'removeClass'](className);
- });
- }
- }
- const tooltip = $link[0]._tippy;
- console.log(tooltip);
- if (ajaxData.templateHtml === '') {
- if (tooltip) {
- tooltip.hide()
- setTimeout(() => {
- $link.data('XenForo.Tooltip', null);
- }, 275)
- }
- }
- else {
- if (tooltip) {
- tooltip.setContent(ajaxData.templateHtml);
- $('.tippy-content').xfActivate();
- }
- else {
- $link.addClass('Tooltip PopupTooltip');
- $('<div>').addClass('TooltipContent').html(ajaxData.templateHtml).insertAfter($link);
- $link.parent().xfActivate();
- $link[0]._tippy.show();
- $link[0]._tippy.setContent(ajaxData.templateHtml);
- $('.tippy-content').xfActivate();
- }
- }
- /*var id = $link.data('container').substr(1);
- $(document.createElement('div')).attr('id', id).html(ajaxData.templateHtml).hide().appendTo('body');
- $link.data('XenForo.Tooltip', null);
- XenForo.register($link, 'XenForo.Tooltip');
- console.log($link.data());*/
- /*if (ajaxData.templateHtml === '') {
- $($link.data('container')).xfFadeUp(XenForo.speed.fast, function () {
- $(this).empty().xfFadeDown(0);
- });
- } else {
- var $container = $($link.data('container')),
- $likeText = $container.find('.LikeText'),
- $templateHtml = $(ajaxData.templateHtml);
- if ($likeText.length) {
- // we already have the likes_summary template in place, so just replace the text
- $likeText.xfFadeOut(50, function () {
- var textContainer = this.parentNode;
- $(this).remove();
- $templateHtml.find('.LikeText').xfInsert('appendTo', textContainer, 'xfFadeIn', 50);
- });
- } else {
- new XenForo.ExtLoader(ajaxData, function () {
- $templateHtml.xfInsert('appendTo', $container);
- });
- }
- }*/
- });
- });
- };
- // *********************************************************************
- XenForo.Facebook =
- {
- initialized: false,
- loading: false,
- parsed: false,
- appId: '',
- fbUid: 0,
- authResponse: {},
- locale: 'en-US',
- init: function () {
- if (XenForo.Facebook.initialized) {
- return;
- }
- XenForo.Facebook.initialized = true;
- XenForo.Facebook.loading = false;
- $(document.body).append($('<div id="fb-root" />'));
- var fbInfo = {
- version: 'v2.10',
- xfbml: false,
- oauth: true,
- channelUrl: XenForo.canonicalizeUrl('fb_channel.php?l=' + XenForo.Facebook.locale)
- };
- if (XenForo.Facebook.appId) {
- fbInfo.appId = XenForo.Facebook.appId;
- }
- FB.init(fbInfo);
- if (XenForo.Facebook.appId && XenForo.Facebook.fbUid) {
- FB.Event.subscribe('auth.authResponseChange', XenForo.Facebook.sessionChange);
- FB.getLoginStatus(XenForo.Facebook.sessionChange);
- if (XenForo.visitor.user_id) {
- $(document).delegate('a.LogOut:not(.OverlayTrigger)', 'click', XenForo.Facebook.eLogOutClick);
- }
- }
- FB.XFBML.parse(null, function () {
- XenForo.Facebook.parsed = true;
- });
- },
- start: function () {
- var cookieUid = $.getCookie('fbUid');
- if (cookieUid && cookieUid.length) {
- XenForo.Facebook.fbUid = parseInt(cookieUid, 10);
- }
- if ($('.fb-post, .fb-video, .fb-page, .fb-like, .fb-share-button').length) {
- XenForo.Facebook.forceInit = true;
- }
- if (!XenForo.Facebook.forceInit && (!XenForo.Facebook.appId || !XenForo.Facebook.fbUid)) {
- return;
- }
- XenForo.Facebook.load();
- },
- load: function () {
- if (XenForo.Facebook.parsed) {
- FB.XFBML.parse();
- return;
- }
- if (XenForo.Facebook.loading) {
- return;
- }
- XenForo.Facebook.loading = true;
- XenForo.Facebook.locale = $('html').attr('lang').replace('-', '_');
- if (!XenForo.Facebook.locale) {
- XenForo.Facebook.locale = 'en_US';
- }
- var e = document.createElement('script'),
- locale = XenForo.Facebook.locale.replace('-', '_');
- e.src = 'https://connect.facebook.net/' + XenForo.Facebook.locale + '/sdk.js';
- e.async = true;
- window.fbAsyncInit = XenForo.Facebook.init;
- document.getElementsByTagName('head')[0].appendChild(e);
- },
- sessionChange: function (response) {
- if (!XenForo.Facebook.fbUid) {
- return;
- }
- var authResponse = response.authResponse, visitor = XenForo.visitor;
- XenForo.Facebook.authResponse = authResponse;
- if (authResponse && !visitor.user_id) {
- if (!XenForo._noSocialLogin) {
- // facebook user, connect!
- XenForo.alert(XenForo.phrases.logging_in + '...', '', 8000);
- setTimeout(function () {
- XenForo.redirect(
- 'register/facebook&t=' + escape(authResponse.accessToken)
- + '&redirect=' + escape(window.location)
- );
- }, 250);
- }
- }
- },
- logout: function (fbData, returnPage) {
- var location = $('a.LogOut:not(.OverlayTrigger)').attr('href');
- if (!location) {
- location = 'logout/&_xfToken=' + XenForo._csrfToken;
- }
- if (returnPage) {
- location += (location.indexOf('?') >= 0 ? '&' : '?') + 'redirect=' + escape(window.location);
- }
- XenForo.redirect(location);
- },
- eLogOutClick: function (e) {
- if (XenForo.Facebook.authResponse && XenForo.Facebook.authResponse.userID) {
- FB.logout(XenForo.Facebook.logout);
- return false;
- }
- }
- };
- // *********************************************************************
- /**
- * Turns an :input into a Prompt
- *
- * @param {Object} :input[placeholder]
- */
- XenForo.Prompt = function ($input) {
- this.__construct($input);
- };
- if ('placeholder' in document.createElement('input')) {
- // native placeholder support
- XenForo.Prompt.prototype =
- {
- __construct: function ($input) {
- this.$input = $input;
- },
- isEmpty: function () {
- return (this.$input.strval() === '');
- },
- val: function (value, focus) {
- if (value === undefined) {
- return this.$input.val();
- } else {
- if (focus) {
- this.$input.focus();
- }
- return this.$input.val(value);
- }
- }
- };
- } else {
- // emulate placeholder support
- XenForo.Prompt.prototype =
- {
- __construct: function ($input) {
- console.log('Emulating placeholder behaviour for %o', $input);
- this.placeholder = $input.attr('placeholder');
- this.$input = $input.bind(
- {
- focus: $.context(this, 'setValueMode'),
- blur: $.context(this, 'setPromptMode')
- });
- this.$input.closest('form').bind(
- {
- submit: $.context(this, 'eFormSubmit'),
- AutoValidationBeforeSubmit: $.context(this, 'eFormSubmit'),
- AutoValidationComplete: $.context(this, 'eFormSubmitted')
- });
- this.setPromptMode();
- },
- /**
- * If the prompt box contains no text, or contains the prompt text (only) it is 'empty'
- *
- * @return boolean
- */
- isEmpty: function () {
- var val = this.$input.val();
- return (val === '' || val == this.placeholder);
- },
- /**
- * When exiting the prompt box, update its contents if necessary
- */
- setPromptMode: function () {
- if (this.isEmpty()) {
- this.$input.val(this.placeholder).addClass('prompt');
- }
- },
- /**
- * When entering the prompt box, clear its contents if it is 'empty'
- */
- setValueMode: function () {
- if (this.isEmpty()) {
- this.$input.val('').removeClass('prompt').select();
- }
- },
- /**
- * Gets or sets the value of the prompt and puts it into the correct mode for its contents
- *
- * @param string value
- */
- val: function (value, focus) {
- // get value
- if (value === undefined) {
- if (this.isEmpty()) {
- return '';
- } else {
- return this.$input.val();
- }
- }
- // clear value
- else if (value === '') {
- this.$input.val('');
- if (focus === undefined) {
- this.setPromptMode();
- }
- }
- // set value
- else {
- this.setValueMode();
- this.$input.val(value);
- }
- },
- /**
- * When the form is submitted, empty the prompt box if it is 'empty'
- *
- * @return boolean true;
- */
- eFormSubmit: function () {
- if (this.isEmpty()) {
- this.$input.val('');
- }
- return true;
- },
- /**
- * Fires immediately after the form has sent its AJAX submission
- */
- eFormSubmitted: function () {
- this.setPromptMode();
- }
- };
- }
- ;
- // *********************************************************************
- /**
- * Turn in input:text.SpinBox into a Spin Box
- * Requires a parameter class of 'SpinBox' and an attribute of 'data-step' with a numeric step value.
- * data-max and data-min parameters are optional.
- *
- * @param {Object} $input
- */
- XenForo.SpinBox = function ($input) {
- this.__construct($input);
- };
- XenForo.SpinBox.prototype =
- {
- __construct: function ($input) {
- var param,
- inputWidth,
- $plusButton,
- $minusButton;
- if ($input.attr('step') === undefined) {
- console.warn('ERROR: No data-step attribute specified for spinbox.');
- return;
- }
- this.parameters = { step: null, min: null, max: null };
- for (param in this.parameters) {
- if ($input.attr(param) === undefined) {
- delete this.parameters[param];
- } else {
- this.parameters[param] = parseFloat($input.attr(param));
- }
- }
- inputWidth = $input.width();
- $plusButton = $('<input type="button" class="button spinBoxButton up" value="+" data-plusminus="+" tabindex="-1" />')
- .insertAfter($input)
- .focus($.context(this, 'eFocusButton'))
- .click($.context(this, 'eClickButton'))
- .mouseenter($.context(this, 'eMouseEnter'))
- .mousedown($.context(this, 'eMousedownButton'))
- .on('mouseleave mouseup', $.context(this, 'eMouseupButton'));
- $minusButton = $('<input type="button" class="button spinBoxButton down" value="-" data-plusminus="-" tabindex="-1" />')
- .insertAfter($plusButton)
- .focus($.context(this, 'eFocusButton'))
- .click($.context(this, 'eClickButton'))
- .mouseenter($.context(this, 'eMouseEnter'))
- .mousedown($.context(this, 'eMousedownButton'))
- .on('mouseleave mouseup', $.context(this, 'eMouseupButton'));
- // set up the input
- this.$input = $input
- .attr('autocomplete', 'off')
- .blur($.context(this, 'eBlurInput'))
- .keyup($.context(this, 'eKeyupInput'));
- // force validation to occur on form submit
- this.$input.closest('form').bind('submit', $.context(this, 'eBlurInput'));
- // initial constraint
- this.$input.val(this.constrain(this.getValue()));
- this.mouseTarget = null;
- },
- /**
- * Returns the (numeric) value of the spinbox
- *
- * @return float
- */
- getValue: function () {
- var value = parseFloat(this.$input.val());
- value = (isNaN(value)) ? parseFloat(this.$input.val().replace(/[^0-9.]/g, '')) : value;
- return (isNaN(value) ? 0 : value);
- },
- /**
- * Asserts that the value of the spinbox is within defined min and max parameters.
- *
- * @param float Spinbox value
- *
- * @return float
- */
- constrain: function (value) {
- if (this.parameters.min !== undefined && value < this.parameters.min) {
- console.warn('Minimum value for SpinBox = %s\n %o', this.parameters.min, this.$input);
- return this.parameters.min;
- } else if (this.parameters.max !== undefined && value > this.parameters.max) {
- console.warn('Maximum value for SpinBox = %s\n %o', this.parameters.max, this.$input);
- return this.parameters.max;
- } else {
- return value;
- }
- },
- /**
- * Takes the value of the SpinBox input to the nearest step.
- *
- * @param string +/- Take the value up or down
- */
- stepValue: function (plusMinus) {
- if (this.$input.prop('readonly')) {
- return false;
- }
- var val = this.getValue(),
- mod = val % this.parameters.step,
- posStep = (plusMinus == '+'),
- newVal = val - mod;
- if (!mod || (posStep && mod > 0) || (!posStep && mod < 0)) {
- newVal = newVal + this.parameters.step * (posStep ? 1 : -1);
- }
- this.$input.val(this.constrain(newVal));
- this.$input.triggerHandler('change');
- },
- /**
- * Handles the input being blurred. Removes the 'pseudofocus' class and constrains the spinbox value.
- *
- * @param Event e
- */
- eBlurInput: function (e) {
- this.$input.val(this.constrain(this.getValue()));
- },
- /**
- * Handles key events on the spinbox input. Up and down arrows perform a value step.
- *
- * @param Event e
- *
- * @return false|undefined
- */
- eKeyupInput: function (e) {
- switch (e.which) {
- case 38: // up
- {
- this.stepValue('+');
- this.$input.select();
- return false;
- }
- case 40: // down
- {
- this.stepValue('-');
- this.$input.select();
- return false;
- }
- }
- },
- /**
- * Handles focus events on spinbox buttons.
- *
- * Does not allow buttons to keep focus, returns focus to the input.
- *
- * @param Event e
- *
- * @return boolean false
- */
- eFocusButton: function (e) {
- return false;
- },
- /**
- * Handles click events on spinbox buttons.
- *
- * The buttons are assumed to have data-plusMinus attributes of + or -
- *
- * @param Event e
- */
- eClickButton: function (e) {
- this.stepValue($(e.target).data('plusminus'));
- this.$input.focus();
- this.$input.select();
- },
- /**
- * Handles a mouse-down event on a spinbox button in order to allow rapid repeats.
- *
- * @param Event e
- */
- eMousedownButton: function (e) {
- this.eMouseupButton(e); // don't orphan
- this.mouseTarget = e.target;
- this.holdTimeout = setTimeout(
- $.context(function () {
- this.holdInterval = setInterval($.context(function () {
- this.stepValue(e.target.value);
- }, this), 75);
- }, this
- ), 500);
- },
- /**
- * Handles re-entering while holding the mouse.
- *
- * @param e
- */
- eMouseEnter: function (e) {
- if (e.which && e.target == this.mouseTarget) {
- this.holdInterval = setInterval($.context(function () {
- this.stepValue(e.target.value);
- }, this), 75);
- }
- },
- /**
- * Handles a mouse-up event on a spinbox button in order to halt rapid repeats.
- *
- * @param Event e
- */
- eMouseupButton: function (e) {
- clearTimeout(this.holdTimeout);
- clearInterval(this.holdInterval);
- if (e.type == 'mouseup') {
- this.mouseTarget = null;
- }
- }
- };
- // *********************************************************************
- /**
- * Allows an input:checkbox or input:radio to disable subsidiary controls
- * based on its own state
- *
- * @param {Object} $input
- */
- XenForo.Disabler = function ($input) {
- /**
- * Sets the disabled state of form elements being controlled by this disabler.
- *
- * @param Event e
- * @param boolean If true, this is the initialization call
- */
- var setStatus = function (e, init) {
- //console.info('Disabler %o for child container: %o', $input, $childContainer);
- var $childControls = $childContainer.find('input, select, textarea, button, .inputWrapper, .taggingInput'),
- speed = init ? 0 : XenForo.speed.fast,
- select = function (e) {
- $childContainer.find('input:not([type=hidden], [type=file]), textarea, select, button').first().focus().select();
- };
- if ($input.is(':checked:enabled')) {
- $childContainer
- .removeAttr('disabled')
- .removeClass('disabled')
- .trigger('DisablerDisabled');
- $childControls
- .removeAttr('disabled')
- .removeClass('disabled');
- if ($input.hasClass('Hider')) {
- if (init) {
- $childContainer.show();
- } else {
- $childContainer.xfFadeDown(speed, init ? null : select);
- }
- } else if (!init) {
- select.call();
- }
- } else {
- if ($input.hasClass('Hider')) {
- if (init) {
- $childContainer.hide();
- } else {
- $childContainer.xfFadeUp(speed, null, speed, 'easeInBack');
- }
- }
- $childContainer
- .prop('disabled', true)
- .addClass('disabled')
- .trigger('DisablerEnabled');
- $childControls
- .prop('disabled', true)
- .addClass('disabled')
- .each(function (i, ctrl) {
- var $ctrl = $(ctrl),
- disabledVal = $ctrl.data('disabled');
- if (disabledVal !== null && typeof (disabledVal) != 'undefined') {
- $ctrl.val(disabledVal);
- }
- });
- }
- },
- $childContainer = $(
- '#' + $input.attr('id') + '_Disabler' + (
- $input.data('disabler-target') ? ', ' + $input.data('disabler-target') : ''
- )
- ),
- $form = $input.closest('form');
- var setStatusDelayed = function () {
- setTimeout(setStatus, 0);
- };
- if ($input.is(':radio')) {
- $form.find('input:radio[name="' + $input.fieldName() + '"]').click(setStatusDelayed);
- } else {
- $input.click(setStatusDelayed);
- }
- $form.bind('reset', setStatusDelayed);
- $form.bind('XFRecalculate', function () {
- setStatus(null, true);
- });
- setStatus(null, true);
- $childContainer.find('label, input, select, textarea').click(function (e) {
- if (!$input.is(':checked')) {
- $input.prop('checked', true);
- setStatus();
- }
- });
- this.setStatus = setStatus;
- };
- // *********************************************************************
- /**
- * Quick way to check or toggle all specified items. Works in one of two ways:
- * 1) If the control is a checkbox, a data-target attribute specified a jQuery
- * selector for a container within which all checkboxes will be toggled
- * 2) If the control is something else, the data-target attribute specifies a
- * jQuery selector for the elements themselves that will be selected.
- *
- * @param jQuery .CheckAll
- */
- XenForo.CheckAll = function ($control) {
- if ($control.is(':checkbox')) {
- var $target = $control.data('target') ? $($control.data('target')) : false;
- if (!$target || !$target.length) {
- $target = $control.closest('form');
- }
- var getCheckBoxes = function () {
- var $checkboxes,
- filter = $control.data('filter');
- $checkboxes = filter
- ? $target.find(filter).filter('input:checkbox')
- : $target.find('input:checkbox');
- return $checkboxes;
- };
- var setSelectAllState = function () {
- var $checkboxes = getCheckBoxes(),
- allSelected = $checkboxes.length > 0;
- $checkboxes.each(function () {
- if ($(this).is($control)) {
- return true;
- }
- if (!$(this).prop('checked')) {
- allSelected = false;
- return false;
- }
- });
- $control.prop('checked', allSelected);
- };
- setSelectAllState();
- var toggleAllRunning = false;
- $target.on('click', 'input:checkbox', function (e) {
- if (toggleAllRunning) {
- return;
- }
- var $target = $(e.target);
- if ($target.is($control)) {
- return;
- }
- if ($control.data('filter')) {
- if (!$target.closest($control.data('filter')).length) {
- return;
- }
- }
- setSelectAllState();
- });
- $control.click(function (e) {
- if (toggleAllRunning) {
- return;
- }
- toggleAllRunning = true;
- getCheckBoxes().prop('checked', e.target.checked).triggerHandler('click');
- toggleAllRunning = false;
- });
- $control.on('XFRecalculate', setSelectAllState);
- } else {
- $control.click(function (e) {
- var target = $control.data('target');
- if (target) {
- $(target).prop('checked', true);
- }
- });
- }
- };
- // *********************************************************************
- /**
- * Method to allow an input (usually a checkbox) to alter the selection of others.
- * When checking the target checkbox, it will also check any controls matching data-check
- * and un-check any controls matching data-uncheck
- *
- * @param jQuery input.AutoChecker[data-check, data-uncheck]
- */
- XenForo.AutoChecker = function ($control) {
- $control.click(function (e) {
- if (this.checked) {
- var selector = null;
- $.each({ check: true, uncheck: false }, function (dataField, checkState) {
- if (selector = $control.data(dataField)) {
- $(selector).each(function () {
- this.checked = checkState;
- var Disabler = $(this).data('XenForo.Disabler');
- if (typeof Disabler == 'object') {
- Disabler.setStatus();
- }
- });
- }
- });
- }
- });
- };
- // *********************************************************************
- /**
- * Converts a checkbox/radio plus label into a toggle button.
- *
- * @param jQuery label.ToggleButton
- */
- XenForo.ToggleButton = function ($label) {
- var $button,
- setCheckedClasses = function () {
- $button[($input.is(':checked') ? 'addClass' : 'removeClass')]('checked');
- },
- $input = $label.hide().find('input:checkbox, input:radio').first(),
- $list = $label.closest('ul, ol').bind('toggleButtonClick', setCheckedClasses);
- if (!$input.length && $label.attr('for')) {
- $input = $('#' + $label.attr('for'));
- }
- $button = $('<a />')
- .text($label.attr('title') || $label.text())
- .insertBefore($label)
- .attr(
- {
- 'class': 'button ' + $label.attr('class'),
- 'title': $label.text()
- })
- .click(function (e) {
- $input.click();
- if ($list.length) {
- $list.triggerHandler('toggleButtonClick');
- } else {
- setCheckedClasses();
- }
- return false;
- });
- $label.closest('form').bind('reset', function (e) {
- setTimeout(setCheckedClasses, 100);
- });
- setCheckedClasses();
- };
- // *********************************************************************
- /**
- * Allows files to be uploaded in-place without a page refresh
- *
- * @param jQuery form.AutoInlineUploader
- */
- /**
- * Allows files to be uploaded in-place without a page refresh
- *
- * @param jQuery form.AutoInlineUploader
- */
- XenForo.AutoInlineUploader = function ($form) {
- /**
- * Fires when the contents of an input:file change.
- * Submits the form into a temporary iframe.
- *
- * @param event e
- */
- var $uploader = $form.find('input:file').each(function () {
- var $target = $(this).change(function (e) {
- if ($(e.target).val() != '') {
- var $iframe,
- $hiddenInput;
- $('.bootstrap-filestyle').find('label').changeElementType('fake_label');
- $iframe = $('<iframe src="about:blank" style="display:none; background-color: white" name="AutoInlineUploader"></iframe>')
- .insertAfter($(e.target))
- .load(function (e) {
- $('.bootstrap-filestyle').find('fake_label').changeElementType('label');
- var $iframe = $(e.target);
- $iframe.contents().find('style').remove();
- var ajaxData = $iframe.contents().text(),
- eComplete = null;
- // Opera fires this function when it's not done with no data
- if (!ajaxData) {
- return false;
- }
- // alert the global progress indicator that the transfer is complete
- $(document).trigger('PseudoAjaxStop');
- $uploader = $uploaderOrig.clone(true).replaceAll($target);
- // removing the iframe after a delay to prevent Firefox' progress indicator staying active
- setTimeout(function () {
- $iframe.remove();
- }, 500);
- try {
- ajaxData = $.parseJSON(ajaxData);
- console.info('Inline file upload completed successfully. Data: %o', ajaxData);
- } catch (e) {
- console.error(ajaxData);
- return false;
- }
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- $('input:submit', this.$form).removeAttr('disabled');
- eComplete = new $.Event('AutoInlineUploadComplete');
- eComplete.$form = $form;
- eComplete.ajaxData = ajaxData;
- $form.trigger(eComplete);
- $('#ctrl_avatar').attr('style', 'position: absolute; clip: rect(0px, 0px, 0px, 0px);');
- $form.xfActivate();
- if (!eComplete.isDefaultPrevented() && (ajaxData.message || ajaxData._redirectMessage)) {
- XenForo.alert(ajaxData.message || ajaxData._redirectMessage, '', 2500);
- }
- });
- $hiddenInput = $('<span>'
- + '<input type="hidden" name="_xfNoRedirect" value="1" />'
- + '<input type="hidden" name="_xfResponseType" value="json-text" />'
- + '<input type="hidden" name="_xfUploader" value="1" />'
- + '</span>')
- .appendTo($form);
- $form.attr('target', 'AutoInlineUploader')
- .submit()
- .trigger('AutoInlineUploadStart');
- $hiddenInput.remove();
- // fire the event that will be caught by the global progress indicator
- $(document).trigger('PseudoAjaxStart');
- $form.find('input:submit').prop('disabled', true);
- }
- }),
- $uploaderOrig = $target.clone(true);
- });
- };
- // *********************************************************************
- XenForo.MultiSubmitFix = function ($form) {
- const selector = 'input:submit, input:reset, input.PreviewButton, input.DisableOnSubmit',
- enable = function () {
- $(window).unbind('pagehide', enable);
- $(document).unbind('ajaxStop', enable)
- $form.trigger('EnableSubmitButtons').find(selector)
- .removeClass('disabled')
- .removeAttr('disabled');
- },
- disable = function () {
- setTimeout(function () {
- /**
- * Workaround for a Firefox issue that prevents resubmission after back button,
- * however the workaround triggers a webkit rendering bug.
- */
- if (!$.browser.webkit) {
- $(window).bind('pagehide', enable);
- }
- $form.trigger('DisableSubmitButtons').find(selector)
- .prop('disabled', true)
- .addClass('disabled');
- }, 0);
- $(document).one('ajaxStop', function () {
- setTimeout(enable, 3000);
- });
- };
- $form.data('MultiSubmitEnable', enable)
- .data('MultiSubmitDisable', disable)
- .submit(disable);
- return enable;
- };
- // *********************************************************************
- /**
- * Handler for radio/checkbox controls that cause the form to submit when they are altered
- *
- * @param jQuery input:radio.SubmitOnChange, input:checkbox.SubmitOnChange, label.SubmitOnChange
- */
- XenForo.SubmitOnChange = function ($input) {
- if ($input.is('label')) {
- $input = $input.find('input:radio, input:checkbox');
- if (!$input.length) {
- return;
- }
- }
- $input.click(function (e) {
- clearTimeout(e.target.form.submitTimeout);
- e.target.form.submitTimeout = setTimeout(function () {
- $(e.target).closest('form').submit();
- }, 500);
- });
- };
- // *********************************************************************
- /**
- * Handler for automatic AJAX form validation and error management
- *
- * Forms to be auto-validated require the following attributes:
- *
- * * data-fieldValidatorUrl: URL of a JSON-returning validator for a single field, using _POST keys of 'name' and 'value'
- * * data-optInOut: (Optional - default = OptOut) Either OptIn or OptOut, depending on the validation mode. Fields with a class of OptIn are included in opt-in mode, while those with OptOut are excluded in opt-out mode.
- * * data-exitUrl: (Optional - no default) If defined, any form reset event will redirect to this URL.
- * * data-existingDataKey: (Optional) Specifies the primary key of the data being manipulated. If this is not present, a hidden input with class="ExistingDataKey" is searched for.
- * * data-redirect: (Optional) If set, the browser will redirect to the returned _redirectTarget from the ajaxData response after validation
- *
- * @param jQuery form.AutoValidator
- */
- XenForo.AutoValidator = function ($form) {
- this.__construct($form);
- };
- XenForo.AutoValidator.prototype =
- {
- __construct: function ($form) {
- this.$form = $form.bind(
- {
- submit: $.context(this, 'ajaxSave'),
- reset: $.context(this, 'formReset'),
- BbCodeWysiwygEditorAutoSave: $.context(this, 'editorAutoSave')
- });
- this.$form.find('input[type="submit"]').click($.context(this, 'setClickedSubmit'));
- this.fieldValidatorUrl = this.$form.data('fieldvalidatorurl');
- this.optInMode = this.$form.data('optinout') || 'OptOut';
- this.ajaxSubmit = (XenForo.isPositive(this.$form.data('normalsubmit')) ? false : true);
- this.submitPending = false;
- this.fieldValidationTimeouts = {};
- this.fieldValidationRequests = {};
- },
- /**
- * Fetches the value of the form's existing data key.
- *
- * This could either be a data-existingDataKey attribute on the form itself,
- * or a hidden input with class 'ExistingDataKey'
- *
- * @return string
- */
- getExistingDataKey: function () {
- var val = this.$form.find('input.ExistingDataKey, select.ExistingDataKey, textarea.ExistingDataKey, button.ExistingDataKey').val();
- if (val === undefined) {
- val = this.$form.data('existingdatakey');
- if (val === undefined) {
- val = '';
- }
- }
- return val;
- },
- /**
- * Intercepts form reset events.
- * If the form specifies a data-exitUrl, the browser will navigate there before resetting the form.
- *
- * @param event e
- */
- formReset: function (e) {
- var exitUrl = this.$form.data('exiturl');
- if (exitUrl) {
- XenForo.redirect(exitUrl);
- }
- },
- /**
- * Fires whenever a submit button is clicked, in order to store the clicked control
- *
- * @param event e
- */
- setClickedSubmit: function (e) {
- this.$form.data('clickedsubmitbutton', e.target);
- },
- editorAutoSave: function (e) {
- if (this.submitPending) {
- e.preventDefault();
- }
- },
- /**
- * Intercepts form submit events.
- * Attempts to save the form with AJAX, after cancelling any pending validation tasks.
- *
- * @param event e
- *
- * @return boolean false
- */
- ajaxSave: function (e) {
- if (!this.ajaxSubmit || !XenForo._enableAjaxSubmit) {
- // do normal validation
- return true;
- }
- this.abortPendingFieldValidation();
- var clickedSubmitButton = this.$form.data('clickedsubmitbutton'),
- serialized,
- $clickedSubmitButton,
- /**
- * Event listeners for this event can:
- * e.preventSubmit = true; to prevent any submission
- * e.preventDefault(); to disable ajax sending
- */
- eDataSend = $.Event('AutoValidationBeforeSubmit');
- eDataSend.formAction = this.$form.attr('action');
- eDataSend.clickedSubmitButton = clickedSubmitButton;
- eDataSend.preventSubmit = false;
- eDataSend.ajaxOptions = {};
- this.$form.trigger(eDataSend);
- this.$form.removeData('clickedSubmitButton');
- if (eDataSend.preventSubmit) {
- return false;
- } else if (!eDataSend.isDefaultPrevented()) {
- serialized = this.$form.serializeArray();
- if (clickedSubmitButton) {
- $clickedSubmitButton = $(clickedSubmitButton);
- if ($clickedSubmitButton.attr('name')) {
- serialized.push({
- name: $clickedSubmitButton.attr('name'),
- value: $clickedSubmitButton.attr('value')
- });
- }
- }
- this.submitPending = true;
- XenForo.ajax(
- eDataSend.formAction,
- serialized,
- $.context(this, 'ajaxSaveResponse'),
- eDataSend.ajaxOptions
- );
- e.preventDefault();
- }
- },
- /**
- * Handles the AJAX response from ajaxSave().
- *
- * @param ajaxData
- * @param textStatus
- * @return
- */
- ajaxSaveResponse: function (ajaxData, textStatus) {
- this.submitPending = false;
- if (!ajaxData) {
- console.warn('No ajax data returned.');
- return false;
- }
- var eDataRecv,
- eError,
- eComplete,
- $trigger;
- eDataRecv = $.Event('AutoValidationDataReceived');
- eDataRecv.ajaxData = ajaxData;
- eDataRecv.textStatus = textStatus;
- eDataRecv.validationError = [];
- console.group('Event: %s', eDataRecv.type);
- this.$form.trigger(eDataRecv);
- console.groupEnd();
- if (eDataRecv.isDefaultPrevented()) {
- return false;
- }
- // if the submission has failed validation, show the error overlay
- if (!this.validates(eDataRecv)) {
- eError = $.Event('AutoValidationError');
- eError.ajaxData = ajaxData;
- eError.textStatus = textStatus;
- eError.validationError = eDataRecv.validationError;
- console.group('Event: %s', eError.type);
- this.$form.trigger(eError);
- console.groupEnd();
- if (eError.isDefaultPrevented()) {
- return false;
- }
- if (this.$form.closest('.xenOverlay').length) {
- this.$form.closest('.xenOverlay').data('overlay').close();
- }
- if (ajaxData.errorTemplateHtml) {
- new XenForo.ExtLoader(ajaxData, function (data) {
- var $overlayHtml = XenForo.alert(
- ajaxData.errorTemplateHtml,
- XenForo.phrases.following_error_occurred + ':'
- );
- if ($overlayHtml) {
- $overlayHtml.find('div.errorDetails').removeClass('baseHtml');
- if (ajaxData.errorOverlayType) {
- $overlayHtml.closest('.errorOverlay').removeClass('errorOverlay').addClass(ajaxData.errorOverlayType);
- }
- }
- });
- } else if (ajaxData.templateHtml) {
- setTimeout($.context(function () {
- this.$error = XenForo.createOverlay(null, this.prepareError(ajaxData.templateHtml)).load();
- }, this), 250);
- } else if (ajaxData.error !== undefined) {
- if (typeof ajaxData.error === 'object') {
- var key;
- for (key in ajaxData.error) {
- break;
- }
- ajaxData.error = ajaxData.error[key];
- }
- XenForo.alert(
- ajaxData.error + '\n'
- + (ajaxData.traceHtml !== undefined ? '<ol class="traceHtml">\n' + ajaxData.traceHtml + '</ol>' : ''),
- XenForo.phrases.following_error_occurred + ':'
- );
- }
- return false;
- }
- eComplete = $.Event('AutoValidationComplete'),
- eComplete.ajaxData = ajaxData;
- eComplete.textStatus = textStatus;
- eComplete.$form = this.$form;
- console.group('Event: %s', eComplete.type);
- this.$form.trigger(eComplete);
- console.groupEnd();
- if (eComplete.isDefaultPrevented()) {
- return false;
- }
- // if the form is in an overlay, close it
- if (this.$form.parents('.xenOverlay').length) {
- var $overlay = this.$form.parents('.xenOverlay').data('overlay');
- if (ajaxData.linkPhrase) {
- $trigger = $overlay.getTrigger();
- $trigger.xfFadeOut(XenForo.speed.fast, function () {
- if (ajaxData.linkUrl && $trigger.is('a')) {
- $trigger.attr('href', ajaxData.linkUrl);
- }
- $trigger
- .text(ajaxData.linkPhrase)
- .xfFadeIn(XenForo.speed.fast);
- });
- }
- // if animations are disabled, decaching can happen too quickly
- setTimeout(function () {
- $overlay.close();
- }, 0);
- }
- if (XenForo.isPositive(this.$form.data('reset'))) {
- this.$form[0].reset();
- }
- if (ajaxData.message) {
- XenForo.alert(ajaxData.message, '', 4000);
- return;
- }
- // if a redirect message was not specified, redirect immediately
- if (ajaxData._redirectMessage == '') {
- this.submitPending = true;
- return this.redirect(ajaxData._redirectTarget);
- }
- // show the redirect message, then redirect if a redirect target was specified
- this.submitPending = true;
- XenForo.alert(ajaxData._redirectMessage, '', 1000, $.context(function () {
- this.redirect(ajaxData._redirectTarget);
- }, this));
- },
- /**
- * Checks for the presence of validation errors in the given event
- *
- * @param event e
- *
- * @return boolean
- */
- validates: function (e) {
- return ($.isEmptyObject(e.validationErrors) && !e.ajaxData.error);
- },
- /**
- * Attempts to match labels to errors for the error overlay
- *
- * @param string html
- *
- * @return jQuery
- */
- prepareError: function (html) {
- $html = $(html);
- // extract labels that correspond to the error fields and insert their text next to the error message
- $html.find('label').each(function (i, label) {
- var $ctrlLabel = $('#' + $(label).attr('for'))
- .closest('.ctrlUnit')
- .find('dt > label');
- if ($ctrlLabel.length) {
- $(label).prepend($ctrlLabel.text() + '<br />');
- }
- });
- return $html;
- },
- /**
- * Redirect the browser to redirectTarget if it is specified
- *
- * @param string redirectTarget
- *
- * @return boolean
- */
- redirect: function (redirectTarget) {
- if (XenForo.isPositive(this.$form.data('redirect')) || !parseInt(XenForo._enableOverlays)) {
- var $AutoValidationRedirect = new $.Event('AutoValidationRedirect');
- $AutoValidationRedirect.redirectTarget = redirectTarget;
- this.$form.trigger($AutoValidationRedirect);
- if (!$AutoValidationRedirect.isDefaultPrevented() && $AutoValidationRedirect.redirectTarget) {
- var fn = function () {
- XenForo.redirect(redirectTarget);
- };
- if (XenForo._manualDeferOverlay) {
- $(document).one('ManualDeferComplete', fn);
- } else {
- fn();
- }
- return true;
- }
- }
- return false;
- },
- // ---------------------------------------------------
- // Field validation methods...
- /**
- * Sets a timeout before an AJAX field validation request will be fired
- * (Prevents AJAX floods)
- *
- * @param string Name of field to be validated
- * @param function Callback to fire when the timeout elapses
- */
- setFieldValidationTimeout: function (name, callback) {
- if (!this.hasFieldValidator(name)) {
- return false;
- }
- console.log('setTimeout %s', name);
- this.clearFieldValidationTimeout(name);
- this.fieldValidationTimeouts[name] = setTimeout(callback, 250);
- },
- /**
- * Cancels a timeout set with setFieldValidationTimeout()
- *
- * @param string name
- */
- clearFieldValidationTimeout: function (name) {
- if (this.fieldValidationTimeouts[name]) {
- console.log('Clear field validation timeout: %s', name);
- clearTimeout(this.fieldValidationTimeouts[name]);
- delete (this.fieldValidationTimeouts[name]);
- }
- },
- /**
- * Fires an AJAX field validation request
- *
- * @param string Name of variable to be verified
- * @param jQuery Input field to be validated
- * @param function Callback function to fire on success
- */
- startFieldValidationRequest: function (name, $input, callback) {
- if (!this.hasFieldValidator(name)) {
- return false;
- }
- // abort any existing AJAX validation requests from this $input
- this.abortFieldValidationRequest(name);
- // fire the AJAX request and register it in the fieldValidationRequests
- // object so it can be cancelled by subsequent requests
- this.fieldValidationRequests[name] = XenForo.ajax(this.fieldValidatorUrl,
- {
- name: name,
- value: $input.fieldValue(),
- existingDataKey: this.getExistingDataKey()
- }, callback,
- {
- global: false // don't show AJAX progress indicators for inline validation
- });
- },
- /**
- * Aborts an AJAX field validation request set up by startFieldValidationRequest()
- *
- * @param string name
- */
- abortFieldValidationRequest: function (name) {
- if (this.fieldValidationRequests[name]) {
- console.log('Abort field validation request: %s', name);
- this.fieldValidationRequests[name].abort();
- delete (this.fieldValidationRequests[name]);
- }
- },
- /**
- * Cancels any pending timeouts or ajax field validation requests
- */
- abortPendingFieldValidation: function () {
- $.each(this.fieldValidationTimeouts, $.context(this, 'clearFieldValidationTimeout'));
- $.each(this.fieldValidationRequests, $.context(this, 'abortFieldValidationRequest'));
- },
- /**
- * Throws a warning if this.fieldValidatorUrl is not valid
- *
- * @param string Name of field to be validated
- *
- * @return boolean
- */
- hasFieldValidator: function (name) {
- if (this.fieldValidatorUrl) {
- return true;
- }
- //console.warn('Unable to request validation for field "%s" due to lack of fieldValidatorUrl in form tag.', name);
- return false;
- }
- };
- // *********************************************************************
- /**
- * Handler for individual fields in an AutoValidator form.
- * Manages individual field validation and inline error display.
- *
- * @param jQuery input [text-type]
- */
- XenForo.AutoValidatorControl = function ($input) {
- this.__construct($input);
- };
- XenForo.AutoValidatorControl.prototype =
- {
- __construct: function ($input) {
- this.$form = $input.closest('form.AutoValidator').bind(
- {
- AutoValidationDataReceived: $.context(this, 'handleFormValidation')
- });
- this.$input = $input.bind(
- {
- change: $.context(this, 'change'),
- AutoValidationError: $.context(this, 'showError'),
- AutoValidationPass: $.context(this, 'hideError')
- });
- this.name = $input.data('validatorname') || $input.attr('name');
- this.autoValidate = $input.hasClass('NoAutoValidate') ? false : true;
- },
- /**
- * When the value of a field changes, initiate validation
- *
- * @param event e
- */
- change: function (e) {
- if (this.autoValidate) {
- this.$form.data('XenForo.AutoValidator')
- .setFieldValidationTimeout(this.name, $.context(this, 'validate'));
- }
- },
- /**
- * Fire a validation AJAX request
- */
- validate: function () {
- if (this.autoValidate) {
- this.$form.data('XenForo.AutoValidator')
- .startFieldValidationRequest(this.name, this.$input, $.context(this, 'handleValidation'));
- }
- },
- /**
- * Handle the data returned from an AJAX validation request fired in validate().
- * Fires 'AutoValidationPass' or 'AutoValidationError' for the $input according to the validation state.
- *
- * @param object ajaxData
- * @param string textStatus
- *
- * @return boolean
- */
- handleValidation: function (ajaxData, textStatus) {
- if (ajaxData && ajaxData.error && ajaxData.error.hasOwnProperty(this.name)) {
- this.$input.trigger({
- type: 'AutoValidationError',
- errorMessage: ajaxData.error[this.name]
- });
- return false;
- } else {
- this.$input.trigger('AutoValidationPass');
- return true;
- }
- },
- /**
- * Shows an inline error message, text contained within a .errorMessage property of the event passed
- *
- * @param event e
- */
- showError: function (e) {
- console.warn('%s: %s', this.name, e.errorMessage);
- var error = this.fetchError(e.errorMessage).css('display', 'inline-block');
- this.positionError(error);
- },
- /**
- * Hides any inline error message shown with this input
- */
- hideError: function () {
- console.info('%s: Okay', this.name);
- if (this.$error) {
- this.fetchError()
- .hide();
- }
- },
- /**
- * Fetches or creates (as necessary) the error HTML object for this field
- *
- * @param string Error message
- *
- * @return jQuery this.$error
- */
- fetchError: function (message) {
- if (!this.$error) {
- this.$error = $('<label for="' + this.$input.attr('id') + '" class="formValidationInlineError">WHoops</label>').insertAfter(this.$input);
- }
- if (message) {
- this.$error.html(message).xfActivate();
- }
- return this.$error;
- },
- /**
- * Returns an object containing top and left properties, used to position the inline error message
- */
- positionError: function ($error) {
- $error.removeClass('inlineError');
- var coords = this.$input.coords('outer', 'position'),
- screenCoords = this.$input.coords('outer'),
- $window = $(window),
- outerWidth = $error.outerWidth(),
- absolute,
- position = { top: coords.top };
- if (!screenCoords.width || !screenCoords.height) {
- absolute = false;
- } else {
- if (XenForo.isRTL()) {
- position.left = coords.left - outerWidth - 10;
- absolute = (screenCoords.left - outerWidth - 10 > 0);
- } else {
- var screenLeft = screenCoords.left + screenCoords.width + 10;
- absolute = screenLeft + outerWidth < ($window.width() + $window.scrollLeft());
- position.left = coords.left + coords.width + 10;
- }
- }
- if (absolute) {
- $error.css(position);
- } else {
- $error.addClass('inlineError');
- }
- },
- /**
- * Handles validation for this field passed down from a submission of the whole AutoValidator
- * form, and passes the relevant data into the handler for this field specifically.
- *
- * @param event e
- */
- handleFormValidation: function (e) {
- if (!this.handleValidation(e.ajaxData, e.textStatus)) {
- e.validationError.push(this.name);
- }
- }
- };
- // *********************************************************************
- /**
- * Checks a form field to see if it is part of an AutoValidator form,
- * and if so, whether or not it is subject to autovalidation.
- *
- * @param object Form control to be tested
- *
- * @return boolean
- */
- XenForo.isAutoValidatorField = function (ctrl) {
- var AutoValidator, $ctrl, $form = $(ctrl.form);
- if (!$form.hasClass('AutoValidator')) {
- return false;
- }
- AutoValidator = $form.data('XenForo.AutoValidator');
- if (AutoValidator) {
- $ctrl = $(ctrl);
- switch (AutoValidator.optInMode) {
- case 'OptIn': {
- return ($ctrl.hasClass('OptIn') || $ctrl.closest('.ctrlUnit').hasClass('OptIn'));
- }
- default: {
- return (!$ctrl.hasClass('OptOut') && !$ctrl.closest('.ctrlUnit').hasClass('OptOut'));
- }
- }
- }
- return false;
- };
- // *********************************************************************
- XenForo.PreviewForm = function ($form) {
- var previewUrl = $form.data('previewurl');
- if (!previewUrl) {
- console.warn('PreviewForm has no data-previewUrl: %o', $form);
- return;
- }
- $form.find('.PreviewButton').click(function (e) {
- var $button = $(this);
- XenForo.ajax(previewUrl, $form.serialize(), function (ajaxData) {
- if (XenForo.hasResponseError(ajaxData) || !XenForo.hasTemplateHtml(ajaxData)) {
- return false;
- }
- new XenForo.ExtLoader(ajaxData, function (ajaxData) {
- var $preview = $form.find('.PreviewContainer').first();
- if ($preview.length) {
- $preview.xfFadeOut(XenForo.speed.fast, function () {
- $preview.html(ajaxData.templateHtml).xfActivate();
- });
- } else {
- $preview = $('<div />', { 'class': 'PreviewContainer' })
- .hide()
- .html(ajaxData.templateHtml)
- .prependTo($form)
- .xfActivate();
- }
- var overlay = $button.data('overlay');
- if (overlay) {
- $preview.show();
- XenForo.createOverlay($preview, $preview.html(ajaxData.templateHtml)).load();
- } else {
- $preview.xfFadeIn(XenForo.speed.fast);
- $preview.get(0).scrollIntoView(true);
- }
- });
- });
- });
- };
- // *********************************************************************
- /**
- * Allows a text input field to rewrite the H1 (or equivalent) tag's contents
- *
- * @param jQuery input[data-liveTitleTemplate]
- */
- XenForo.LiveTitle = function ($input) {
- var $title = $input.closest('.formOverlay').find('h2.h1'), setTitle;
- if (!$title.length) {
- $title = $('.titleBar h1').first();
- }
- console.info('Title Element: %o', $title);
- $title.data('originalhtml', $title.html());
- setTitle = function (value) {
- $input.trigger('LiveTitleSet', [value]);
- $title.html(value === ''
- ? $title.data('originalhtml')
- : $input.data('livetitletemplate').replace(/%s/, $('<div />').text(value).html())
- );
- };
- if (!$input.hasClass('prompt')) {
- setTitle($input.strval());
- }
- $('.modal').on('hidden.bs.modal', function () {
- setTitle('')
- })
- $input.bind('keyup focus', function (e) {
- setTitle($input.strval());
- })
- .on('paste', function (e) {
- setTimeout(function () {
- setTitle($input.strval());
- }, 0);
- })
- .closest('form').bind('reset', function (e) {
- setTitle('');
- });
- };
- // *********************************************************************
- XenForo.TextareaElastic = function ($input) {
- this.__construct($input);
- };
- XenForo.TextareaElastic.prototype =
- {
- __construct: function ($input) {
- this.$input = $input;
- this.curHeight = 0;
- $input.bind('keyup focus XFRecalculate', $.context(this, 'recalculate'));
- $input.bind('paste', $.context(this, 'paste'));
- if ($input.val() !== '') {
- this.recalculate();
- }
- },
- recalculate: function () {
- var $input = this.$input,
- input = $input.get(0),
- clone,
- height,
- pos;
- if ($input.val() === '') {
- $input.css({
- 'overflow-y': 'hidden',
- 'height': ''
- });
- this.curHeight = 0;
- return;
- }
- if (!input.clientWidth) {
- return;
- }
- if (!this.minHeight) {
- this.borderBox = ($input.css('-moz-box-sizing') == 'border-box' || $input.css('box-sizing') == 'border-box');
- this.minHeight = (this.borderBox ? $input.outerHeight() : input.clientHeight);
- if (!this.minHeight) {
- return;
- }
- this.maxHeight = parseInt($input.css('max-height'), 10);
- this.spacing = (this.borderBox ? $input.outerHeight() - $input.innerHeight() : 0);
- }
- if (!this.$clone) {
- this.$clone = $('<textarea />').css({
- position: 'absolute',
- left: (XenForo.isRTL() ? '9999em' : '-9999em'),
- top: 0,
- visibility: 'hidden',
- width: input.clientWidth,
- height: '1px',
- 'font-size': $input.css('font-size'),
- 'font-family': $input.css('font-family'),
- 'font-weight': $input.css('font-weight'),
- 'line-height': $input.css('line-height'),
- 'word-wrap': $input.css('word-wrap')
- }).attr('tabindex', -1).val(' ');
- this.$clone.appendTo(document.body);
- this.lineHeight = this.$clone.get(0).scrollHeight;
- }
- this.$clone.val($input.val());
- clone = this.$clone.get(0);
- height = Math.max(this.minHeight, clone.scrollHeight + this.lineHeight + this.spacing);
- if (height < this.maxHeight) {
- if (this.curHeight != height) {
- input = $input.get(0);
- if (this.curHeight == this.maxHeight && input.setSelectionRange) {
- pos = input.selectionStart;
- }
- $input.css({
- 'overflow-y': 'hidden',
- 'height': height + 'px'
- });
- if (this.curHeight == this.maxHeight && input.setSelectionRange) {
- try {
- input.setSelectionRange(pos, pos);
- } catch (e) {
- }
- }
- this.curHeight = height;
- }
- } else {
- if (this.curHeight != this.maxHeight) {
- input = $input.get(0);
- if (input.setSelectionRange) {
- pos = input.selectionStart;
- }
- $input.css({
- 'overflow-y': 'auto',
- 'height': this.maxHeight + 'px'
- });
- if (input.setSelectionRange) {
- try {
- input.setSelectionRange(pos, pos);
- } catch (e) {
- }
- }
- this.curHeight = this.maxHeight;
- }
- }
- },
- paste: function () {
- setTimeout($.context(this, 'recalculate'), 100);
- }
- };
- // *********************************************************************
- XenForo.AutoTimeZone = function ($element) {
- var now = new Date(),
- jan1 = new Date(now.getFullYear(), 0, 1), // 0 = jan
- jun1 = new Date(now.getFullYear(), 5, 1), // 5 = june
- jan1offset = Math.round(jan1.getTimezoneOffset()),
- jun1offset = Math.round(jun1.getTimezoneOffset());
- // opera doesn't report TZ offset differences in jan/jun correctly
- if ($.browser.opera) {
- return false;
- }
- if (XenForo.AutoTimeZone.map[jan1offset + ',' + jun1offset]) {
- $element.val(XenForo.AutoTimeZone.map[jan1offset + ',' + jun1offset]);
- return true;
- } else {
- return false;
- }
- };
- XenForo.AutoTimeZone.map =
- {
- '660,660': 'Pacific/Midway',
- '600,600': 'Pacific/Honolulu',
- '570,570': 'Pacific/Marquesas',
- '540,480': 'America/Anchorage',
- '480,420': 'America/Los_Angeles',
- '420,360': 'America/Denver',
- '420,420': 'America/Phoenix',
- '360,300': 'America/Chicago',
- '360,360': 'America/Belize',
- '300,240': 'America/New_York',
- '300,300': 'America/Bogota',
- '270,270': 'America/Caracas',
- '240,180': 'America/Halifax',
- '180,240': 'America/Cuiaba',
- '240,240': 'America/La_Paz',
- '210,150': 'America/St_Johns',
- '180,180': 'America/Argentina/Buenos_Aires',
- '120,180': 'America/Sao_Paulo',
- '180,120': 'America/Miquelon',
- '120,120': 'America/Noronha',
- '60,60': 'Atlantic/Cape_Verde',
- '60,0': 'Atlantic/Azores',
- '0,-60': 'Europe/London',
- '0,0': 'Atlantic/Reykjavik',
- '-60,-120': 'Europe/Amsterdam',
- '-60,-60': 'Africa/Algiers',
- '-120,-60': 'Africa/Windhoek',
- '-120,-180': 'Europe/Athens',
- '-120,-120': 'Africa/Johannesburg',
- '-180,-240': 'Africa/Nairobi',
- '-180,-180': 'Europe/Moscow',
- '-210,-270': 'Asia/Tehran',
- '-240,-300': 'Asia/Yerevan',
- '-270,-270': 'Asia/Kabul',
- '-300,-300': 'Asia/Tashkent',
- '-330,-330': 'Asia/Kolkata',
- '-345,-345': 'Asia/Kathmandu',
- '-360,-360': 'Asia/Dhaka',
- '-390,-390': 'Asia/Rangoon',
- '-420,-420': 'Asia/Bangkok',
- '-420,-480': 'Asia/Krasnoyarsk',
- '-480,-480': 'Asia/Hong_Kong',
- '-540,-540': 'Asia/Tokyo',
- '-630,-570': 'Australia/Adelaide',
- '-570,-570': 'Australia/Darwin',
- '-660,-600': 'Australia/Sydney',
- '-600,-600': 'Asia/Vladivostok',
- '-690,-690': 'Pacific/Norfolk',
- '-780,-720': 'Pacific/Auckland',
- '-825,-765': 'Pacific/Chatham',
- '-780,-780': 'Pacific/Tongatapu',
- '-840,-840': 'Pacific/Kiritimati'
- };
- // *********************************************************************
- XenForo.DatePicker = function ($input) {
- if (!XenForo.DatePicker.$root) {
- $.tools.dateinput.localize('_f',
- {
- months: XenForo.phrases._months,
- shortMonths: '1,2,3,4,5,6,7,8,9,10,11,12',
- days: 's,m,t,w,t,f,s',
- shortDays: XenForo.phrases._daysShort
- });
- }
- var $date = $input.dateinput(
- {
- lang: '_f',
- format: 'yyyy-mm-dd', // rfc 3339 format, required by html5 date element
- speed: 0,
- yearRange: [-100, 100],
- onShow: function (e) {
- var $root = XenForo.DatePicker.$root,
- offset = $date.offset(),
- maxZIndex = 0,
- position = { top: offset.top + $date.outerHeight() };
- if (XenForo.isRTL()) {
- position.right = $('html').width() - offset.left - $date.outerWidth();
- } else {
- position.left = offset.left;
- }
- $root.css(position);
- $date.parents().each(function (i, el) {
- var zIndex = parseInt($(el).css('z-index'), 10);
- if (zIndex > maxZIndex) {
- maxZIndex = zIndex;
- }
- });
- $root.css('z-index', maxZIndex + 1000);
- }
- });
- $date.addClass($input.attr('class'));
- if ($input.attr('id')) {
- $date.attr('id', $input.attr('id'));
- }
- // this is needed to handle input[type=reset] buttons that end up focusing the field
- $date.closest('form').on('reset', function () {
- setTimeout(function () {
- $date.data('dateinput').hide();
- }, 10);
- setTimeout(function () {
- $date.data('dateinput').hide();
- }, 100);
- });
- if (!XenForo.DatePicker.$root) {
- XenForo.DatePicker.$root = $('#calroot').appendTo(document.body);
- $('#calprev').html(XenForo.isRTL() ? '→' : '←').prop('unselectable', true);
- $('#calnext').html(XenForo.isRTL() ? '←' : '→').prop('unselectable', true);
- }
- };
- XenForo.DatePicker.$root = null;
- // *********************************************************************
- var localStorageSmiliesKey = 'smilies_cache';
- XenForo.AutoComplete = function ($element) {
- this.__construct($element);
- };
- XenForo.AutoComplete.prototype =
- {
- __construct: function ($input) {
- this.$input = $input;
- this.url = $input.data('acurl') || XenForo.AutoComplete.getDefaultUrl();
- this.extraFields = $input.data('acextrafields');
- this.smiliesAutoCompleteCount = 5;
- var options = {
- multiple: $input.hasClass('AcSingle') ? false : ',', // mutiple value joiner
- minLength: 2, // min word length before lookup
- queryKey: 'q',
- extraParams: { searchOnlyWithAtSign: $input.hasClass('AtSign') },
- jsonContainer: 'results',
- autoSubmit: XenForo.isPositive($input.data('autosubmit'))
- };
- if ($input.data('acoptions')) {
- options = $.extend(options, $input.data('acoptions'));
- }
- if (options.autoSubmit) {
- options.multiple = false;
- }
- this.multiple = options.multiple;
- this.minLength = options.minLength;
- this.queryKey = options.queryKey;
- this.extraParams = options.extraParams;
- this.jsonContainer = options.jsonContainer;
- this.autoSubmit = options.autoSubmit;
- this.enabled = true
- var self = this
- this.loadVal = '';
- this.lastAcLookup = []
- this.results = new XenForo.AutoCompleteResults({
- onInsert: $.context(this, 'addValue')
- });
- this.acSmiliesResults = new XenForo.AutoSmiliesCompleteResults({
- onInsert: $.context(this, 'insertAutoComplete')
- });
- $input.attr('autocomplete', 'off')
- .keydown($.context(this, 'keystroke'))
- .keypress($.context(this, 'operaKeyPress'))
- .blur($.context(this, 'blur'));
- $input.on('paste', function () {
- setTimeout(function () {
- $input.trigger('keydown');
- }, 0);
- });
- $input.closest('form').submit($.context(this, 'hideResults'));
- var smilies_loaded = false;
- if (supports_html5_storage()) {
- var temp = localStorage.getItem(localStorageSmiliesKey);
- if (temp) {
- temp = JSON.parse(temp)
- if (temp.cache === XenForo.smiliesCacheKey) {
- this.smilies_complete = temp
- smilies_loaded = true
- }
- }
- if (!smilies_loaded) {
- XenForo.ajax(
- XenForo._editorSmiliesUrl,
- {},
- function (ajaxData) {
- if (XenForo.hasResponseError(ajaxData)) {
- return;
- }
- var data = {}
- data.smilies = ajaxData.smilies
- data.cache = XenForo.smiliesCacheKey
- if (temp) {
- data.recently = temp.recently
- } else {
- data.recently = []
- }
- localStorage.setItem(localStorageSmiliesKey, JSON.stringify(data))
- self.smilies_complete = data
- }).complete(function () {
- this.smilies_complete = self.smilies_complete
- }.bind(this));
- }
- }
- if (this.$input.hasClass('MessageCtrl')) {
- $(".ImNotification--QuickReplyForm").on('submit', function (e) {
- self.hideAutoComplete();
- })
- } else {
- $("input[type=submit]").on('click', function () {
- self.hideAutoComplete();
- })
- }
- },
- setEnabled(enabled) {
- if (this.loadTimer) clearTimeout(this.loadTimer)
- this.loadTimer = null
- this.hideResults()
- this.enabled = enabled
- },
- keystroke: function (e) {
- if (!this.enabled) return
- var code = e.keyCode || e.charCode, prevent = true;
- switch (code) {
- case 40:
- if (this.results.isVisible()) {
- this.results.selectResult(1);
- break; // down
- }
- case 39:
- if (this.acSmiliesResults.isVisible()) {
- this.acSmiliesResults.selectResult(1);
- break; // right
- }
- case 38:
- if (this.results.isVisible()) {
- this.results.selectResult(-1);
- break; // up
- }
- case 37:
- if (this.acSmiliesResults.isVisible()) {
- this.acSmiliesResults.selectResult(-1);
- break; // left
- }
- case 27:
- if (this.results.isVisible()) {
- this.results.hideResults();
- } else {
- if (this.acSmiliesResults.isVisible()) {
- this.acSmiliesResults.hideResults()
- } else {
- prevent = false;
- }
- }
- break; // esc
- case 13: // enter
- if (this.results.isVisible()) {
- this.results.insertSelectedResult();
- } else {
- if (this.acSmiliesResults.isVisible() && this.acSmiliesResults.selectedResult !== -1) {
- this.acSmiliesResults.insertSelectedResult()
- } else {
- prevent = false;
- }
- }
- this.hideAutoComplete();
- break;
- default:
- prevent = false;
- if (e.ctrlKey && code === 86 || code === 17) {
- return
- }
- if (this.loadTimer) {
- clearTimeout(this.loadTimer);
- }
- this.loadTimer = setTimeout($.context(this, 'load'), 200);
- this.loadTimerSmilies = setTimeout($.context(this, 'smilies'), 1);
- if (code != 229) {
- this.results.hideResults();
- }
- }
- if (prevent) {
- e.preventDefault();
- }
- this.preventKey = prevent;
- },
- operaKeyPress: function (e) {
- if ($.browser.opera && this.preventKey) {
- e.preventDefault();
- }
- },
- blur: function (e) {
- clearTimeout(this.loadTimer);
- // timeout ensures that clicks still register
- setTimeout($.context(this, 'hideResults'), 250);
- if (this.xhr) {
- this.xhr.abort();
- this.xhr = false;
- }
- },
- smilies: function () {
- if (!this.$input.hasClass('AutoSmiliesComplete')) {
- return
- }
- if (!(XenForo.SmiliesAutoComplete && !XenForo.SmiliesAutoComplete.enable)) {
- var smiliesCompleteText = this.findCurrentSmiliesAutoCompleteOption();
- var smiliesFullCompleteText = this.findFullSmiliesAutoCompleteOption();
- } else {
- var smiliesCompleteText = []
- var smiliesFullCompleteText = []
- }
- if (smiliesCompleteText.length) {
- return this.triggerSmiliesAutoComplete(smiliesCompleteText);
- } else {
- if (smiliesFullCompleteText.length) {
- return this.triggerSmiliesAutoComplete(smiliesFullCompleteText, true);
- }
- }
- return this.hideAutoComplete();
- },
- hideAutoComplete: function () {
- this.acSmiliesResults.hideResults();
- },
- load: function () {
- var lastLoad = this.loadVal,
- params = this.extraParams;
- if (this.loadTimer) {
- clearTimeout(this.loadTimer);
- }
- this.loadVal = this.getPartialValue();
- if (this.loadVal === '') {
- this.hideResults();
- return;
- }
- if (params.searchOnlyWithAtSign) {
- if (this.loadVal.indexOf('@') !== 0) {
- return;
- }
- this.loadVal = this.loadVal.substr(1);
- }
- if (this.loadVal == lastLoad) {
- return;
- }
- if (this.loadVal.length < this.minLength) {
- return;
- }
- if (this.loadVal.indexOf(",") !== -1) {
- return;
- }
- params[this.queryKey] = this.loadVal;
- if (this.extraFields != '') {
- $(this.extraFields).each(function () {
- params[this.name] = $(this).val();
- });
- }
- if (this.xhr) {
- this.xhr.abort();
- }
- this.xhr = XenForo.ajax(
- this.url,
- params,
- $.context(this, 'showResults'),
- { global: false, error: false }
- );
- },
- hideResults: function () {
- this.results.hideResults();
- },
- showResults: function (results) {
- if (this.xhr) {
- this.xhr = false;
- }
- if (this.jsonContainer && results) {
- results = results[this.jsonContainer];
- }
- this.results.showResults(this.getPartialValue(), results, this.$input);
- },
- addValue: function (value) {
- if (this.extraParams.searchOnlyWithAtSign) {
- value = '@' + value;
- }
- if (!this.multiple) {
- this.$input.val(value + ' ');
- } else {
- var values = this.getFullValues();
- if (value != '') {
- if (values.length) {
- value = ' ' + value;
- }
- values.push(value + this.multiple + ' ');
- }
- this.$input.val(values.join(this.multiple));
- }
- this.$input.trigger("AutoComplete", { inserted: value, current: this.$input.val() });
- if (this.autoSubmit) {
- this.$input.closest('form').submit();
- } else {
- this.$input.focus();
- }
- },
- findFullSmiliesAutoCompleteOption: function () {
- var selected = window.getSelection()
- var origin = [selected.anchorNode, selected.anchorOffset];
- var focus = [selected.focusNode, selected.focusOffset]
- this.loadVal = this.getPartialValue();
- testText = this.loadVal
- var lastAt = testText.lastIndexOf(':');
- if (lastAt < testText.lastIndexOf('@')) {
- return false
- }
- var list = []
- // Search by full name
- list.push(...this.smilies_complete.smilies.filter(function (e) {
- return e.name.toUpperCase() === testText.toUpperCase() // By name
- || e.value.toUpperCase() === testText.toUpperCase() // By value
- || e.aliases.filter(function (alias) {
- return alias.toUpperCase() === testText.toUpperCase() // By alias
- }).length
- }
- ))
- list = list.filter(function (e) { return e.value !== name.value })
- list.splice(this.smiliesAutoCompleteCount)
- return list
- },
- findCurrentSmiliesAutoCompleteOption: function () {
- var selected = window.getSelection()
- var origin = [selected.anchorNode, selected.anchorOffset];
- var focus = [selected.focusNode, selected.focusOffset]
- this.loadVal = this.getPartialValue();
- testText = this.loadVal
- var lastAt = testText.lastIndexOf(':');
- if (lastAt < testText.lastIndexOf('@')) {
- return false
- }
- var list = []
- if (lastAt !== -1 && (lastAt === 0 || ((testText[lastAt - 1].trim() === "" || testText[lastAt + 1]) && !(/[a-zA-Z0-9]/).test(testText[lastAt - 1]))) && list.length < this.smiliesAutoCompleteCount) {
- var afterAt = testText.substr(lastAt + 1);
- afterAt = afterAt.replace(new RegExp(String.fromCharCode(160), 'g'), ' ')
- if (afterAt.startsWith(' ')) {
- return false;
- }
- if (!afterAt.trim() && this.smilies_complete.recently.length) {
- // Return recently
- list = this.smilies_complete.recently;
- }
- list.push(...this.smilies_complete.smilies.filter(function (e) {
- return e.name.replaceAll(":", "").substr(0, afterAt.length).toUpperCase() === afterAt.toUpperCase() // By name
- || e.value.replaceAll(":", "").substr(0, afterAt.length).toUpperCase() === afterAt.toUpperCase() // By value
- || e.aliases.filter(function (alias) {
- return alias.replaceAll(":", "").substr(0, afterAt.length).toUpperCase() === afterAt.toUpperCase() // By alias
- }).length
- }
- ))
- }
- list = list.filter(function (e) { return e.value !== name.value })
- list.splice(this.smiliesAutoCompleteCount)
- return list
- },
- insertAutoComplete: function (name) {
- this.$input.focus();
- var selected = window.getSelection()
- var origin = [selected.anchorNode, selected.anchorOffset];
- var focus = [selected.focusNode, selected.focusOffset]
- if (!focus || !origin || focus[0] != origin[0] || focus[1] != origin[1] || typeof this.smilies_complete === "undefined") {
- return false;
- }
- var $focus = $(focus[0]),
- testText = focus[0].nodeType == 3 ? $focus.text().substring(0, focus[1]) : $($focus.contents().get(focus[1] - 1)).val();
- if (!testText.trim()) {
- testText = $($focus.contents().get(focus[1])).val()
- }
- var lastAt = testText.toLowerCase().lastIndexOf(name[0].toLowerCase());
- if (lastAt === -1) {
- lastAt = 0
- }
- var name = this.lastAcLookup[parseInt(name[1])]
- var value = name.value
- var input = this.$input.val()
- var length = input.length
- input = input.substring(0, lastAt)
- input = input.substring(length - (lastAt + testText.length))
- input += value
- this.$input.val(input)
- this.$input.trigger("AutoComplete", { inserted: value, current: this.$input.val() });
- if (this.autoSubmit) {
- this.$input.closest('form').submit();
- } else {
- this.$input.focus();
- }
- this.lastAcLookup = name;
- // Save to recently
- var recently = this.smilies_complete.recently.filter(function (e) { return e.value !== name.value })
- recently.unshift(name)
- recently.splice(this.smiliesAutoCompleteCount)
- this.smilies_complete.recently = recently
- if (supports_html5_storage()) {
- localStorage.setItem(localStorageSmiliesKey, JSON.stringify(this.smilies_complete))
- }
- this.hideAutoComplete();
- this.$input.focus();
- },
- triggerSmiliesAutoComplete: function (name, def) {
- if (this.lastAcLookup.length && this.lastAcLookup == name) {
- return;
- }
- if (this.$input.prop('disabled')) {
- return
- }
- this.hideAutoComplete();
- this.lastAcLookup = name;
- this.showAutoCompleteResults({ results: name }, def)
- },
- showAutoCompleteResults: function (ajaxData, def) {
- this.acXhr = false;
- if (JSON.stringify(this.lastAcLookup) !== JSON.stringify(this.findCurrentSmiliesAutoCompleteOption()) && JSON.stringify(this.lastAcLookup) !== JSON.stringify(this.findFullSmiliesAutoCompleteOption())) {
- return;
- }
- this.acSmiliesResults.showResults(
- this.lastAcLookup,
- ajaxData.results,
- this.$input
- );
- if (def) {
- this.acSmiliesResults.selectResult(-1)
- }
- },
- getFullValues: function () {
- var val = this.$input.val();
- if (val == '') {
- return [];
- }
- if (!this.multiple) {
- return [val];
- } else {
- splitPos = val.lastIndexOf(this.multiple);
- if (splitPos == -1) {
- return [];
- } else {
- val = val.substr(0, splitPos);
- return val.split(this.multiple);
- }
- }
- },
- getPartialValue: function () {
- var val = this.$input.val(),
- splitPos;
- if (!this.multiple) {
- return $.trim(val);
- } else {
- splitPos = val.lastIndexOf(this.multiple);
- let value
- if (splitPos == -1) {
- value = $.trim(val);
- } else {
- value = $.trim(val.substr(splitPos + this.multiple.length));
- }
- return value.replace(/^@/, '')
- }
- }
- };
- XenForo.AutoComplete.getDefaultUrl = function () {
- if (XenForo.AutoComplete.defaultUrl === null) {
- if ($('html').hasClass('Admin')) {
- XenForo.AutoComplete.defaultUrl = 'admin.php?users/search-name&_xfResponseType=json';
- } else {
- XenForo.AutoComplete.defaultUrl = 'members/find&_xfResponseType=json';
- }
- }
- ;
- return XenForo.AutoComplete.defaultUrl;
- };
- XenForo.AutoComplete.defaultUrl = null;
- // *********************************************************************
- XenForo.UserTagger = function ($element) {
- this.__construct($element);
- };
- XenForo.UserTagger.prototype =
- {
- __construct: function ($textarea) {
- this.$textarea = $textarea;
- this.url = $textarea.data('acurl') || XenForo.AutoComplete.getDefaultUrl();
- this.acResults = new XenForo.AutoCompleteResults({
- onInsert: $.context(this, 'insertAutoComplete')
- });
- var self = this,
- hideTimer,
- hideCallback = function () {
- if (hideTimer) {
- return;
- }
- hideTimer = setTimeout(function () {
- self.acResults.hideResults();
- hideTimer = null;
- }, 200);
- };
- $(document).on('scroll', hideCallback);
- $textarea.on('click blur', hideCallback);
- $textarea.on('keydown', function (e) {
- var prevent = true,
- acResults = self.acResults;
- if (!acResults.isVisible()) {
- return;
- }
- switch (e.keyCode) {
- case 40:
- acResults.selectResult(1);
- break; // down
- case 38:
- acResults.selectResult(-1);
- break; // up
- case 27:
- acResults.hideResults();
- break; // esc
- case 13:
- acResults.insertSelectedResult();
- break; // enter
- default:
- prevent = false;
- }
- if (prevent) {
- e.stopPropagation();
- e.stopImmediatePropagation();
- e.preventDefault();
- }
- });
- $textarea.on('keyup', function (e) {
- var autoCompleteText = self.findCurrentAutoCompleteOption();
- if (autoCompleteText) {
- self.triggerAutoComplete(autoCompleteText);
- } else {
- self.hideAutoComplete();
- }
- });
- },
- findCurrentAutoCompleteOption: function () {
- var $textarea = this.$textarea;
- $textarea.focus();
- var sel = $textarea.getSelection(),
- testText,
- lastAt;
- if (!sel || sel.end <= 1) {
- return false;
- }
- testText = $textarea.val().substring(0, sel.end);
- lastAt = testText.lastIndexOf('@');
- if (lastAt != -1 && (lastAt == 0 || testText.substr(lastAt - 1, 1).match(/(\s|[\](,]|--)/))) {
- var afterAt = testText.substr(lastAt + 1);
- if (!afterAt.match(/\s/) || afterAt.length <= 10) {
- return afterAt;
- }
- }
- return false;
- },
- insertAutoComplete: function (name) {
- var $textarea = this.$textarea;
- $textarea.focus();
- var sel = $textarea.getSelection(),
- testText;
- if (!sel || sel.end <= 1) {
- return false;
- }
- testText = $textarea.val().substring(0, sel.end);
- var lastAt = testText.lastIndexOf('@');
- if (lastAt != -1) {
- $textarea.setSelection(lastAt, sel.end);
- $textarea.replaceSelectedText('@' + name + ' ', 'collapseToEnd');
- this.lastAcLookup = name + ' ';
- }
- },
- triggerAutoComplete: function (name) {
- if (this.lastAcLookup && this.lastAcLookup == name) {
- return;
- }
- this.hideAutoComplete();
- this.lastAcLookup = name;
- if (name.length > 2 && name.substr(0, 1) != '[') {
- this.acLoadTimer = setTimeout($.context(this, 'autoCompleteLookup'), 200);
- }
- },
- autoCompleteLookup: function () {
- if (this.acXhr) {
- this.acXhr.abort();
- }
- if (this.lastAcLookup != this.findCurrentAutoCompleteOption()) {
- return;
- }
- this.acXhr = XenForo.ajax(
- this.url,
- { q: this.lastAcLookup },
- $.context(this, 'showAutoCompleteResults'),
- { global: false, error: false }
- );
- },
- showAutoCompleteResults: function (ajaxData) {
- this.acXhr = false;
- this.acResults.showResults(
- this.lastAcLookup,
- ajaxData.results,
- this.$textarea
- );
- },
- hideAutoComplete: function () {
- this.acResults.hideResults();
- if (this.acLoadTimer) {
- clearTimeout(this.acLoadTimer);
- this.acLoadTimer = false;
- }
- }
- };
- // *********************************************************************
- XenForo.AutoCompleteResults = function (options) {
- this.__construct(options);
- };
- XenForo.AutoCompleteResults.prototype =
- {
- __construct: function (options) {
- this.options = $.extend({
- onInsert: false
- }, options);
- this.selectedResult = 0;
- this.$results = false;
- this.resultsVisible = false;
- this.resizeBound = false;
- },
- isVisible: function () {
- return this.resultsVisible;
- },
- hideResults: function () {
- this.resultsVisible = false;
- if (this.$results) {
- this.$results.hide();
- }
- },
- showResults: function (val, results, $targetOver, cssPosition) {
- var maxZIndex = 0,
- i,
- filterRegex,
- result,
- $li;
- if (!results) {
- this.hideResults();
- return;
- }
- this.resultsVisible = false;
- if (!this.$results) {
- this.$results = $('<ul />')
- .css({ position: 'absolute', display: 'none' })
- .addClass('autoCompleteList')
- .appendTo(document.body);
- $targetOver.parents().each(function (i, el) {
- var $el = $(el),
- zIndex = parseInt($el.css('z-index'), 10);
- if (zIndex > maxZIndex) {
- maxZIndex = zIndex;
- }
- });
- this.$results.css('z-index', maxZIndex + 1000);
- this.$results.data('XenForo.AutoCompleteResults', this);
- } else {
- this.$results.hide().empty();
- }
- //filterRegex = new RegExp('(' + XenForo.regexQuote(val) + ')', 'i');
- for (i in results) {
- if (!results.hasOwnProperty(i)) {
- continue;
- }
- result = results[i];
- var $html = $(result.username)
- $html.find('.scamNotice').remove()
- var $avatar = $html.clone();
- $avatar.prepend($(`<img class="smallCompleteAvatar" src="${result.avatar}" style="float: left;margin-top: 2px;margin-right: 5px;border-radius: 17px"/>`))
- var $lastSpan = $html.find('span:last')
- $lastSpan.text('@' + $lastSpan.text())
- $li = $('<li />')
- .css('cursor', 'pointer')
- .attr('unselectable', 'on')
- .data('autocomplete', i)
- .data('autocompleteHtml', $html.prop('outerHTML'))
- .data('autocompleteAvatar', $avatar.prop('outerHTML'))
- .click($.context(this, 'resultClick'))
- .mouseenter($.context(this, 'resultMouseEnter'))
- .on('mousedown touchstart', function (e) {
- e.stopPropagation() // prevent focusing, fix for froala popups
- })
- if (typeof result == 'string') {
- $li.html(XenForo.htmlspecialchars(result));//.replace(filterRegex, '<strong>$1</strong>'));
- } else {
- // unescaping the avatar here as we expect the username to be escaped, so this means
- // that we can/should expect all values to be escaped
- $li.html(result['username'])
- .prepend(
- $('<img class="autoCompleteAvatar" />')
- .attr('src', XenForo.htmlEntityDecode(result['avatar']))
- );
- /*$li.html(result['username'].replace(filterRegex, '<strong>$1</strong>'))
- .prepend(
- $('<img class="autoCompleteAvatar" />')
- .attr('src', XenForo.htmlEntityDecode(result['avatar']))
- );*/
- }
- $li.appendTo(this.$results);
- }
- if (!this.$results.find('li').length) {
- return;
- }
- this.selectResult(0, true);
- if (!this.resizeBound) {
- $(window).bind('resize', $.context(this, 'hideResults'));
- }
- if (!cssPosition) {
- var offset = $targetOver.offset();
- cssPosition = {
- top: offset.top + $targetOver.outerHeight(),
- left: offset.left
- };
- if (XenForo.isRTL()) {
- cssPosition.right = $('html').width() - offset.left - $targetOver.outerWidth();
- cssPosition.left = 'auto';
- }
- }
- this.$results.css(cssPosition).show();
- this.$results.wrapInner('<div class="scrollbar scrollbar-macosx scrollbar-dynamic"></div>');
- this.$results.children().scrollbar();
- this.resultsVisible = true;
- $(window).on('scroll', function (e) {
- var offset = $targetOver.offset();
- cssPosition = {
- top: offset.top + $targetOver.outerHeight(),
- left: offset.left
- };
- if (XenForo.isRTL()) {
- cssPosition.right = $('html').width() - offset.left - $targetOver.outerWidth();
- cssPosition.left = 'auto';
- }
- this.$results.css(cssPosition)
- var rect = this.$results.get(0).getBoundingClientRect();
- if (!(rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth))) {
- if (!cssPosition.focus) {
- cssPosition.focus = 40;
- }
- cssPosition.top -= this.$results.get(0).offsetHeight;
- cssPosition.top -= cssPosition.focus;
- if (cssPosition.top < 0 && XenForo.isTouchBrowser()) return;
- this.$results.css(cssPosition);
- }
- }.bind(this));
- var rect = this.$results.get(0).getBoundingClientRect();
- if (!(rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth))) {
- if (!cssPosition.focus) {
- cssPosition.focus = 40;
- }
- cssPosition.top -= this.$results.get(0).offsetHeight;
- cssPosition.top -= cssPosition.focus;
- if (cssPosition.top < 0 && XenForo.isTouchBrowser()) return;
- this.$results.css(cssPosition);
- }
- },
- resultClick: function (e) {
- e.stopPropagation();
- this.insertResult($(e.currentTarget).data('autocomplete'), $(e.currentTarget).data('autocompleteHtml'), $(e.currentTarget).data('autocompleteAvatar'));
- this.hideResults();
- },
- resultMouseEnter: function (e) {
- this.selectResult($(e.currentTarget).index(), true);
- },
- selectResult: function (shift, absolute) {
- var sel, children;
- if (!this.$results) {
- return;
- }
- if (absolute) {
- this.selectedResult = shift;
- } else {
- this.selectedResult += shift;
- }
- sel = this.selectedResult;
- children = this.$results.find('li');
- children.each(function (i) {
- if (i == sel) {
- $(this).addClass('selected');
- } else {
- $(this).removeClass('selected');
- }
- });
- if (sel < 0 || sel >= children.length) {
- this.selectedResult = -1;
- }
- },
- insertSelectedResult: function () {
- var res, ret = false;
- if (!this.resultsVisible) {
- return false;
- }
- if (this.selectedResult >= 0) {
- res = this.$results.find('li').get(this.selectedResult);
- if (res) {
- this.insertResult($(res).data('autocomplete'), $(res).data('autocompleteHtml'), $(res).data('autocompleteAvatar'));
- ret = true;
- }
- }
- this.hideResults();
- return ret;
- },
- insertResult: function (value, htmlValue, avatarValue) {
- if (this.options.onInsert) {
- this.options.onInsert(value, htmlValue, avatarValue);
- }
- }
- };
- XenForo.AutoSmiliesCompleteResults = function (options) { this.__construct(options); };
- XenForo.AutoSmiliesCompleteResults.prototype =
- {
- __construct: function (options) {
- this.options = $.extend({
- onInsert: false
- }, options);
- this.selectedResult = 0;
- this.$results = false;
- this.resultsVisible = false;
- this.resizeBound = false;
- },
- isVisible: function () {
- return this.resultsVisible;
- },
- hideResults: function () {
- this.resultsVisible = false;
- if (this.$results) {
- this.$results.hide();
- }
- },
- showResults: function (val, results, $targetOver, cssPosition) {
- var maxZIndex = 0,
- i,
- filterRegex,
- result,
- $li;
- if (!results) {
- this.hideResults();
- return;
- }
- this.resultsVisible = false;
- if (!this.$results) {
- this.$results = $('<ul />')
- .css({
- position: 'absolute', display: 'none',
- 'background': 'rgb(45, 45, 45)', 'font-size': '13px', 'border-radius': '3px'
- })
- .addClass('autoCompleteListSmilies')
- .appendTo(document.body);
- $targetOver.parents().each(function (i, el) {
- var $el = $(el),
- zIndex = parseInt($el.css('z-index'), 10);
- if (zIndex > maxZIndex) {
- maxZIndex = zIndex;
- }
- });
- this.$results.css('z-index', maxZIndex + 1000);
- this.$results.data('XenForo.AutoSmiliesCompleteResults', this);
- }
- else {
- this.$results.hide().empty();
- }
- filterRegex = new RegExp('(' + XenForo.regexQuote(val) + ')', 'i');
- for (var i = 0; i < results.length; i++) {
- if (!results.hasOwnProperty(i)) {
- continue;
- }
- result = results[i];
- $li = $('<li />')
- .css('cursor', 'pointer')
- .css('float', 'left')
- .css('box-sizing', 'border-box')
- .css('line-height', '15px')
- .css("display", "flex")
- .css("justify-content", 'center')
- .css('width', '50px')
- .css('height', '50px')
- .attr('unselectable', 'on')
- .data('autocomplete', i)
- .click($.context(this, 'resultClick'))
- .mouseenter($.context(this, 'resultMouseEnter'));
- $listElement = $('<li />', { 'class': 'Smilie' }).css('display', 'flex').css('align-items', 'center')
- $listElement.append($('<img />', { 'src': result.image, title: result.name, alt: result.value, 'data-smilie': 'yes' }).css('max-width', '46px'))
- $li.html($listElement.get(0).outerHTML)
- $li.appendTo(this.$results);
- }
- if (!this.$results.children().length) {
- return;
- }
- this.selectResult(0, true);
- if (!this.resizeBound) {
- $(window).bind('resize', $.context(this, 'hideResults'));
- }
- if (!cssPosition) {
- var offset = $targetOver.offset();
- cssPosition = {
- top: offset.top + $targetOver.outerHeight(),
- left: offset.left
- };
- if (XenForo.isRTL()) {
- cssPosition.right = $('html').width() - offset.left - $targetOver.outerWidth();
- cssPosition.left = 'auto';
- }
- }
- this.$results.css(cssPosition);
- this.$results.show()
- if (!isElementInViewport(this.$results)) {
- if (!cssPosition.focus) {
- cssPosition.focus = 40
- }
- cssPosition.top -= this.$results.get(0).offsetHeight
- cssPosition.top -= cssPosition.focus
- this.$results.css(cssPosition);
- }
- $(window).on('scroll', function (e) {
- var offset = $targetOver.offset();
- cssPosition = {
- top: offset.top + $targetOver.outerHeight(),
- left: offset.left
- };
- if (XenForo.isRTL()) {
- cssPosition.right = $('html').width() - offset.left - $targetOver.outerWidth();
- cssPosition.left = 'auto';
- }
- this.$results.css(cssPosition)
- var rect = this.$results.get(0).getBoundingClientRect();
- if (!(rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth))) {
- if (!cssPosition.focus) {
- cssPosition.focus = 40
- }
- cssPosition.top -= this.$results.get(0).offsetHeight
- cssPosition.top -= cssPosition.focus
- this.$results.css(cssPosition);
- }
- }.bind(this));
- this.resultsVisible = true;
- },
- resultClick: function (e) {
- e.stopPropagation();
- this.insertResult($(e.currentTarget).data('autocomplete'));
- this.hideResults();
- },
- resultMouseEnter: function (e) {
- this.selectResult($(e.currentTarget).index(), true);
- },
- selectResult: function (shift, absolute) {
- var sel, children;
- if (!this.$results) {
- return;
- }
- if (absolute) {
- this.selectedResult = shift;
- }
- else {
- this.selectedResult += shift;
- }
- sel = this.selectedResult;
- children = this.$results.children();
- if (sel >= children.length) {
- this.selectedResult = 0;
- sel = 0;
- }
- if (sel < -1) {
- return this.hideResults()
- }
- children.each(function (i) {
- if (i == sel) {
- $(this).css('background', 'rgb(54, 54, 54)')
- $(this).addClass('selected');
- }
- else {
- $(this).css('background', 'rgb(45, 45, 45)')
- $(this).removeClass('selected');
- }
- });
- },
- insertSelectedResult: function () {
- var res, ret = false;
- if (!this.resultsVisible) {
- return false;
- }
- if (this.selectedResult >= 0) {
- res = this.$results.children().get(this.selectedResult);
- if (res) {
- this.insertResult($(res).data('autocomplete'));
- ret = true;
- }
- }
- this.hideResults();
- return ret;
- },
- insertResult: function (value) {
- if (this.options.onInsert) {
- this.options.onInsert(":" + value);
- }
- }
- };
- XenForo.AutoTemplateCompleteResults = function (options) { this.__construct(options); };
- XenForo.AutoTemplateCompleteResults.prototype =
- {
- __construct: function (options) {
- this.options = $.extend({
- onInsert: false
- }, options);
- this.selectedResult = 0;
- this.$results = false;
- this.resultsVisible = false;
- this.resizeBound = false;
- },
- isVisible: function () {
- return this.resultsVisible;
- },
- hideResults: function () {
- this.resultsVisible = false;
- if (this.$results) {
- this.$results.hide();
- }
- },
- showResults: function (val, results, $targetOver, cssPosition) {
- var maxZIndex = 0,
- i,
- filterRegex,
- result,
- $li;
- if (!results) {
- this.hideResults();
- return;
- }
- this.resultsVisible = false;
- if (!this.$results) {
- this.$results = $('<ul />')
- .css({ position: 'absolute', display: 'none' })
- .addClass('autoCompleteList')
- .appendTo(document.body);
- $targetOver.parents().each(function (i, el) {
- var $el = $(el),
- zIndex = parseInt($el.css('z-index'), 10);
- if (zIndex > maxZIndex) {
- maxZIndex = zIndex;
- }
- });
- this.$results.css('z-index', maxZIndex + 1000);
- }
- else {
- this.$results.hide().empty();
- }
- filterRegex = new RegExp('(' + XenForo.regexQuote(val) + ')', 'i');
- for (var i = 0; i < results.length; i++) {
- if (!results.hasOwnProperty(i)) {
- continue;
- }
- result = results[i];
- $li = $('<li />')
- .css('cursor', 'pointer')
- .attr('unselectable', 'on')
- .data('autocomplete', i)
- .click($.context(this, 'resultClick'))
- .mouseenter($.context(this, 'resultMouseEnter'));
- $listElement = $('<div />', { 'class': 'InsertTemplate' })
- $listElement.append($('<div />', { 'class': 'title bold', text: result['title'] }))
- $listElement.append($('<div />', { 'class': 'content', text: result['content'] }))
- $listElement.append($('<div />', { 'class': 'ContentHtml dnone', text: result['text'] }))
- $li.html($listElement.get(0).outerHTML)
- $li.appendTo(this.$results);
- }
- if (!this.$results.children().length) {
- return;
- }
- this.selectResult(0, true);
- if (!this.resizeBound) {
- $(window).bind('resize', $.context(this, 'hideResults'));
- }
- if (!cssPosition) {
- var offset = $targetOver.offset();
- cssPosition = {
- top: offset.top + $targetOver.outerHeight(),
- left: offset.left
- };
- if (XenForo.isRTL()) {
- cssPosition.right = $('html').width() - offset.left - $targetOver.outerWidth();
- cssPosition.left = 'auto';
- }
- }
- this.$results.css(cssPosition);
- this.$results.show()
- if (!isElementInViewport(this.$results)) {
- if (!cssPosition.focus) {
- cssPosition.focus = 40
- }
- cssPosition.top -= this.$results.get(0).offsetHeight
- cssPosition.top -= cssPosition.focus
- this.$results.css(cssPosition);
- }
- $(window).on('scroll', function (e) {
- var offset = $targetOver.offset();
- cssPosition = {
- top: offset.top + $targetOver.outerHeight(),
- left: offset.left
- };
- if (XenForo.isRTL()) {
- cssPosition.right = $('html').width() - offset.left - $targetOver.outerWidth();
- cssPosition.left = 'auto';
- }
- this.$results.css(cssPosition)
- var rect = this.$results.get(0).getBoundingClientRect();
- if (!(rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth))) {
- if (!cssPosition.focus) {
- cssPosition.focus = 40
- }
- cssPosition.top -= this.$results.get(0).offsetHeight
- cssPosition.top -= cssPosition.focus
- this.$results.css(cssPosition);
- }
- }.bind(this));
- this.resultsVisible = true;
- },
- resultClick: function (e) {
- e.stopPropagation();
- this.insertResult($(e.currentTarget).data('autocomplete'));
- this.hideResults();
- },
- resultMouseEnter: function (e) {
- this.selectResult($(e.currentTarget).index(), true);
- },
- selectResult: function (shift, absolute) {
- var sel, children;
- if (!this.$results) {
- return;
- }
- if (absolute) {
- this.selectedResult = shift;
- }
- else {
- this.selectedResult += shift;
- }
- sel = this.selectedResult;
- children = this.$results.children();
- if (sel < 0 || sel >= children.length) {
- this.selectedResult = 0;
- sel = 0;
- }
- children.each(function (i) {
- if (i == sel) {
- $(this).addClass('selected');
- }
- else {
- $(this).removeClass('selected');
- }
- });
- },
- insertSelectedResult: function () {
- var res, ret = false;
- if (!this.resultsVisible) {
- return false;
- }
- if (this.selectedResult >= 0) {
- res = this.$results.children().get(this.selectedResult);
- if (res) {
- this.insertResult($(res).data('autocomplete'));
- ret = true;
- }
- }
- this.hideResults();
- return ret;
- },
- insertResult: function (value) {
- if (this.options.onInsert) {
- this.options.onInsert(value);
- }
- }
- };
- // *********************************************************************
- XenForo.AutoSelect = function ($input) {
- $input.bind('focus', function (e) {
- setTimeout(function () {
- $input.select();
- }, 50);
- });
- };
- // *********************************************************************
- /**
- * Status Editor
- *
- * @param jQuery $textarea.StatusEditor
- */
- XenForo.StatusEditor = function ($input) {
- this.__construct($input);
- };
- XenForo.StatusEditor.prototype =
- {
- __construct: function ($input) {
- this.$input = $input
- .keyup($.context(this, 'update'))
- .keydown($.context(this, 'preventNewline'));
- this.$counter = $(this.$input.data('statuseditorcounter'));
- if (!this.$counter.length) {
- this.$counter = $('<span />').insertAfter(this.$input);
- }
- this.$counter
- .addClass('statusEditorCounter')
- .text('0');
- this.$form = this.$input.closest('form').bind(
- {
- AutoValidationComplete: $.context(this, 'saveStatus')
- });
- this.charLimit = 140; // Twitter max characters
- this.charCount = 0; // number of chars currently in use
- this.update();
- },
- /**
- * Handles key events on the status editor, updates the 'characters remaining' output.
- *
- * @param Event e
- */
- update: function (e) {
- var statusText = this.$input.val();
- if (this.$input.attr('placeholder') && this.$input.attr('placeholder') == statusText) {
- this.setCounterValue(this.charLimit, statusText.length);
- } else {
- this.setCounterValue(this.charLimit - statusText.length, statusText.length);
- }
- },
- /**
- * Sets the value of the character countdown, and appropriate classes for that value.
- *
- * @param integer Characters remaining
- * @param integer Current length of status text
- */
- setCounterValue: function (remaining, length) {
- if (remaining < 0) {
- this.$counter.addClass('error');
- this.$counter.removeClass('warning');
- } else if (remaining <= this.charLimit - 130) {
- this.$counter.removeClass('error');
- this.$counter.addClass('warning');
- } else {
- this.$counter.removeClass('error');
- this.$counter.removeClass('warning');
- }
- this.$counter.text(remaining);
- this.charCount = length || this.$input.val().length;
- },
- /**
- * Don't allow newline characters in the status message.
- *
- * Submit the form if [Enter] or [Return] is hit.
- *
- * @param Event e
- */
- preventNewline: function (e) {
- if (e.which == 13) // return / enter
- {
- e.preventDefault();
- $(this.$input.get(0).form).submit();
- return false;
- }
- },
- /**
- * Updates the status field after saving
- *
- * @param event e
- */
- saveStatus: function (e) {
- this.$input.val('');
- this.update(e);
- if (e.ajaxData && e.ajaxData.status !== undefined) {
- $('.CurrentStatus').text(e.ajaxData.status);
- }
- }
- };
- // *********************************************************************
- window.addEventListener("scroll", function () {
- window.lastScrollTime = new Date().getTime()
- });
- function is_scrolling() {
- return window.lastScrollTime && new Date().getTime() < window.lastScrollTime + 500
- }
- XenForo._ShowPreviewTimeout = null;
- XenForo._ActivePreviewTooltip = null;
- XenForo.PreviewTooltip = function ($el, timeout, appendTo) {
- var previewUrl;
- if (!parseInt(XenForo._enableOverlays)) {
- return;
- }
- if (!(previewUrl = $el.data('previewurl'))) {
- console.warn('Preview tooltip has no preview: %o', $el);
- return;
- }
- $el.find('[title]').andSelf().attr('title', '');
- var loaded = false;
- tippy($el.get(), {
- touch: false,
- interactive: false,
- arrow: true,
- theme: timeout ? 'popup' : 'popup PreviewTooltip',
- animation: 'shift-toward',
- distance: 5,
- appendTo: appendTo || document.body,
- delay: [timeout || 300, 0],
- maxWidth: 400,
- placement: 'top-start',
- flipOnUpdate: true,
- content: '',
- popperOptions: {
- modifiers: {
- computeStyle: {
- gpuAcceleration: false
- }
- }
- },
- onShow(instance) {
- if (is_scrolling()) {
- clearTimeout(XenForo._ShowPreviewTimeout);
- XenForo._ShowPreviewTimeout = setTimeout(function () {
- console.log('check scroll', is_scrolling());
- if (!is_scrolling()) {
- console.log('trigger hover', $el[0]._tippy);
- $el[0]._tippy.show();
- }
- }, 700);
- return false;
- }
- if (XenForo._ActivePreviewTooltip && XenForo._ActivePreviewTooltip !== instance) {
- XenForo._ActivePreviewTooltip.hide();
- }
- if (!loaded) {
- XenForo.ajax(previewUrl, {}, function (ajaxData) {
- var $content = $('#PreviewTooltip').clone();
- $content.find('.previewContent').html('');
- //$content.hide();
- $(ajaxData.templateHtml).xfInsert('appendTo', $content.find('.previewContent'), 'fadeIn', 50, function () {
- instance.setContent($content.html());
- loaded = true;
- if ($el.is(':hover')) {
- instance.show();
- XenForo._ActivePreviewTooltip = instance;
- return true;
- }
- });
- });
- return false;
- }
- return true;
- },
- })
- };
- // *********************************************************************
- /**
- * Allows an entire block to act as a link in the navigation popups
- *
- * @param jQuery li.PopupItemLink
- */
- XenForo.PopupItemLink = function ($listItem) {
- var href = $listItem.find('.PopupItemLink').first().attr('href');
- if (href) {
- $listItem
- .addClass('PopupItemLinkActive')
- .on('click auxclick contextmenu', function (e) {
- if ($(e.target).closest('a').length || $(e.target).closest('input').length || window.getSelection().toString() !== "" || $(e.target).closest('.marketIndexItem--otherInfo').length || $(e.target).closest('.marketItemView--loginData').length) {
- return;
- }
- switch (e.which) {
- case 1: XenForo.redirect(href); break
- case 2: window.open(href)
- }
- });
- }
- };
- // *********************************************************************
- /**
- * Allows a link or input to load content via AJAX and insert it into the DOM.
- * The control element to which this is applied must have href or data-href attributes
- * and a data-target attribute describing a jQuery selector for the element relative to which
- * the content will be inserted.
- *
- * You may optionally provide a data-method attribute to override the default insertion method
- * of 'appendTo'.
- *
- * By default, the control will be unlinked and have its click event unbound after a single use.
- * Specify data-unlink="false" to prevent this default behaviour.
- *
- * Upon successful return of AJAX data, the control element will fire a 'ContentLoaded' event,
- * including ajaxData and textStatus data properties.
- */
- XenForo.Loader = function ($link) {
- var clickHandler = function (e) {
- var href = $link.attr('href') || $link.data('href'),
- target = $link.data('target');
- if (href && $(target).length) {
- if ($link.closest('a').length) {
- e.stopPropagation();
- }
- e.preventDefault();
- if ($link.data('tooltip')) {
- $link.data('tooltip').hide();
- }
- XenForo.ajax(href, {}, function (ajaxData, textStatus) {
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- var insertEvent = new $.Event('ContentLoaded');
- insertEvent.ajaxData = ajaxData;
- insertEvent.textStatus = textStatus;
- $link.trigger(insertEvent);
- if (!insertEvent.isDefaultPrevented()) {
- if (ajaxData.templateHtml) {
- new XenForo.ExtLoader(ajaxData, function () {
- var method = $link.data('method');
- if (typeof $.fn[method] != 'function') {
- method = 'appendTo';
- }
- if (method == 'replaceAll') {
- $(ajaxData.templateHtml).xfInsert(method, target, 'show', 0);
- } else {
- $(ajaxData.templateHtml).xfInsert(method, target);
- }
- if ($link.data('unlink') !== false) {
- $link.removeAttr('href').removeData('href').unbind('click', clickHandler);
- }
- });
- }
- }
- });
- }
- };
- $link.bind('click', clickHandler);
- };
- // *********************************************************************
- /**
- * Allows a control to create a clone of an existing field, like 'add new response' for polls
- *
- * @param jQuery $button.FieldAdder[data-source=#selectorOfCloneSource]
- */
- XenForo.FieldAdder = function ($button) {
- $($button.data('source')).filter('.PollNonJsInput').remove();
- var maxFields = $button.data('maxfields');
- var checkRemoveButton = function (instant) {
- if (maxFields && $($button.data('source')).length >= maxFields) {
- if (instant) {
- $button.css('display', 'none');
- } else {
- $button.xfHide();
- }
- } else {
- $button.css('display', '').xfShow();
- }
- };
- // https://zelenka.guru/threads/4615364/
- var $source = $($button.data('source'))
- var $template = $source.last().clone()
- var $insertTarget = $('<div>').hide().insertAfter($source.last())
- $button.click(function (e) {
- if ((!maxFields || ($($button.data('source')).length < maxFields))) {
- var $clone = $template.clone();
- $clone.find('span.button').on('click', function () {
- checkRemoveButton()
- })
- $clone.find('input:not([type="button"], [type="submit"])').val('').prop('disabled', true);
- $clone.find('.spinBoxButton').remove();
- $button.trigger({
- type: 'FieldAdderClone',
- clone: $clone
- });
- $clone.xfInsert('insertBefore', $insertTarget, false, false, function () {
- checkRemoveButton();
- var $inputs = $clone.find('input');
- $inputs.prop('disabled', false);
- $inputs.first().focus().select();
- });
- } else {
- checkRemoveButton();
- }
- });
- checkRemoveButton(true);
- };
- // *********************************************************************
- /**
- * Quick way to toggle the read status of an item
- *
- * @param jQuery a.ReadToggle
- */
- XenForo.ReadToggle = function ($link) {
- $link.click(function (e) {
- e.preventDefault();
- var xhr = null,
- $items = null,
- counterId = $link.data('counter');
- if (xhr == null) {
- $items = $link.closest('.discussionListItem').andSelf().toggleClass('unread');
- xhr = XenForo.ajax($link.attr('href'), { _xfConfirm: 1 }, function (ajaxData, textStatus) {
- xhr = null;
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- if (typeof ajaxData.unread != 'undefined') {
- $items[(ajaxData.unread ? 'addClass' : 'removeClass')]('unread');
- }
- if (counterId && typeof ajaxData.counterFormatted != 'undefined') {
- var $counter = $(counterId),
- $total = $counter.find('span.Total');
- if ($total.length) {
- $total.text(ajaxData.counterFormatted);
- } else {
- $counter.text(ajaxData.counterFormatted);
- }
- }
- if (typeof ajaxData.actionPhrase != 'undefined') {
- if ($link.text() != '') {
- $link.html(ajaxData.actionPhrase);
- }
- if ($link.attr('title')) {
- $link.attr('title', ajaxData.actionPhrase);
- }
- }
- XenForo.alert(ajaxData._redirectMessage, '', 1000);
- });
- }
- });
- };
- // *********************************************************************
- XenForo.Notices = function ($notices) {
- var useCookie = XenForo.visitor.user_id ? false : true,
- cookieName = 'notice_dismiss',
- useScroller = $notices.hasClass('PanelScroller');
- var getCookieIds = function () {
- var cookieValue = $.getCookie(cookieName),
- cookieDismissed = cookieValue ? cookieValue.split(',') : [],
- values = [],
- id;
- for (var i = 0; i < cookieDismissed.length; i++) {
- id = parseInt(cookieDismissed[i], 10);
- if (id) {
- values.push(id);
- }
- }
- return values;
- };
- var dismissed = getCookieIds();
- if (useCookie) {
- $notices.find('.Notice').each(function () {
- var $notice = $(this),
- id = parseInt($notice.data('notice'), 10);
- if (id && $.inArray(id, dismissed) != -1) {
- $notice.remove();
- $('#n' + id).remove();
- }
- });
- }
- var setNoticeVisibility = function ($notices, useScroller) {
- if (useScroller) {
- $notices.find('.Notice').each(function () {
- var $notice = $(this),
- display = $(this).css('display'),
- id = parseInt($notice.data('notice'), 10);
- if (display == 'none') {
- $notice.remove();
- $('#n' + id).remove();
- }
- });
- var $navLinks = $notices.find('.Nav a');
- if (!$navLinks.filter('.current').length) {
- $navLinks.first().addClass('current');
- }
- }
- if (!$notices.find('.Notice').length) {
- $notices.remove();
- return;
- }
- };
- setNoticeVisibility($notices, useScroller);
- $notices.show();
- var PanelScroller;
- if (useScroller) {
- PanelScroller = XenForo.PanelScroller($notices.find('.PanelContainer'), {
- scrollable: {
- speed: $notices.dataOrDefault('speed', 400) * XenForo._animationSpeedMultiplier,
- vertical: XenForo.isPositive($notices.data('vertical')),
- keyboard: false,
- touch: false,
- prev: '.NoticePrev',
- next: '.NoticeNext'
- },
- autoscroll: { interval: $notices.dataOrDefault('interval', 2000) }
- });
- if (PanelScroller && PanelScroller.getItems().length > 1) {
- $(document).bind({
- XenForoWindowBlur: function (e) {
- PanelScroller.stop();
- },
- XenForoWindowFocus: function (e) {
- PanelScroller.play();
- }
- });
- }
- }
- if ($notices.hasClass('FloatingContainer')) {
- var $floatingNotices = $notices.find('.Notice');
- $floatingNotices.each(function () {
- var $self = $(this),
- displayDuration = $self.data('display-duration'),
- delayDuration = $self.data('delay-duration'),
- autoDismiss = XenForo.isPositive($self.data('auto-dismiss'));
- if (delayDuration) {
- setTimeout(function () {
- $self.xfFadeDown(XenForo.speed.normal, function () {
- if (displayDuration) {
- setTimeout(function () {
- $self.xfFadeUp(XenForo.speed.normal);
- if (autoDismiss) {
- $self.find('a.DismissCtrl').trigger('click');
- }
- }, displayDuration);
- }
- });
- }, delayDuration);
- } else {
- $self.css('display', 'block');
- if (displayDuration) {
- setTimeout(function () {
- $self.xfFadeUp(XenForo.speed.normal);
- if (autoDismiss) {
- $self.find('a.DismissCtrl').trigger('click');
- }
- }, displayDuration);
- }
- }
- });
- }
- var $cookieNotice = $notices.find('.Notice[data-notice="-1"]');
- if ($cookieNotice.length) {
- var height = $cookieNotice.height();
- $(document).find('footer').css('margin-bottom', height);
- }
- $notices.delegate('a.DismissCtrl, a.CustomDismissCtrl', 'click', function (e) {
- e.preventDefault();
- var $ctrl = $(this),
- $notice = $ctrl.closest('.Notice'),
- $noticeParent = $notice.closest('.Notices');
- if ($ctrl.data('tooltip')) {
- $ctrl.data('tooltip').hide();
- }
- if (PanelScroller) {
- PanelScroller.removeItem($notice);
- if (!PanelScroller.getItems().length) {
- $notices.xfFadeUp();
- }
- } else {
- $notice.xfFadeUp(XenForo.speed.fast, function () {
- if ($notice.data('notice') == '-1') {
- $(document).find('footer').css('margin-bottom', '');
- }
- $notice.remove();
- if (!$noticeParent.find('.Notice').length) {
- $notices.xfFadeUp();
- }
- });
- }
- if (useCookie) {
- var noticeId = parseInt($notice.data('notice'), 10),
- dismissed = getCookieIds();
- if (noticeId && $.inArray(noticeId, dismissed) == -1) {
- dismissed.push(noticeId);
- dismissed.sort(function (a, b) {
- return (a - b);
- });
- $.setCookie(cookieName, dismissed.join(','));
- }
- } else if (!$ctrl.data('xhr')) {
- $ctrl.data('xhr', XenForo.ajax($ctrl.attr('href'), { _xfConfirm: 1 }, function (ajaxData, textStatus) {
- $ctrl.removeData('xhr');
- if (XenForo.hasResponseError(ajaxData)) {
- return false;
- }
- //XenForo.alert(ajaxData._redirectMessage, '', 2000);
- }));
- }
- });
- $(window).on('resize', function (e) {
- setNoticeVisibility($notices, useScroller);
- });
- };
- // *********************************************************************
- XenForo.PanelScroller = function ($container, options) {
- var $items = $container.find('.Panels > *');
- // don't initialize if we have just a single panel
- if ($items.length < 2) {
- $container.find('.Panels').css('position', 'static');
- return false;
- }
- $items.find('script').remove(); // script should already have been run and document.writes break stuff
- function resizeItems() {
- var maxHeight = 0;
- $container.find('.Panels > *').css({ width: $container.innerWidth(), height: 'auto' }).each(function () {
- maxHeight = Math.max($(this).outerHeight(), maxHeight);
- }).andSelf().css('height', maxHeight);
- var api = $container.data('scrollable');
- if (api) {
- api.seekTo(api.getIndex(), 0);
- }
- };
- options = $.extend(true,
- {
- scrollable:
- {
- circular: true,
- items: '.Panels'
- },
- navigator:
- {
- navi: '.Nav',
- naviItem: 'a',
- activeClass: 'current'
- },
- autoscroll:
- {
- interval: 3000
- }
- }, options);
- $container.css('overflow', 'hidden');
- if (!options.scrollable.vertical) {
- $container.css('height', 'auto')
- .find('.Panels').css('width', '20000em')
- .find('.panel').css('float', (XenForo.isRTL() ? 'right' : 'left'));
- }
- $(window).bind('load resize', resizeItems);
- $('.mainContent').bind('XenForoResize', resizeItems);
- resizeItems();
- $container.scrollable(options.scrollable).navigator(options.navigator);
- if ($items.length > 1) {
- $container.autoscroll(options.autoscroll);
- }
- $container.find('input.Disabler').on('click', function () {
- setInterval(resizeItems, XenForo.speed.fast);
- });
- return $container.data('scrollable');
- };
- // *********************************************************************
- XenForo.DisplayIgnoredContent = function (e) {
- var i, j, styleSheet, rules, rule;
- const isProfilePage = $('.profilePage').length;
- e.preventDefault();
- let $hideContent = $('a.DisplayIgnoredContent')
- $hideContent.hide();
- // remove the styling that hides quotes etc.
- $('#ignoredUserCss').empty().remove();
- if (document.styleSheets) {
- for (i = 0; i < document.styleSheets.length; i++) {
- styleSheet = document.styleSheets[i];
- try {
- rules = (styleSheet.cssRules ? styleSheet.cssRules : styleSheet.rules);
- } catch (e) {
- rules = false;
- }
- if (!rules) {
- continue;
- }
- for (j = 0; j < rules.length; j++) {
- rule = rules[j];
- if (rule.selectorText && rule.selectorText.toLowerCase() === '.ignored') {
- if (styleSheet.deleteRule) {
- styleSheet.deleteRule(j);
- } else {
- styleSheet.removeRule(j);
- }
- }
- }
- }
- }
- $('.ignored').removeClass('ignored');
- if (isProfilePage && !$hideContent.closest('.pageNavLinkGroup').find('.PageNav').length) {
- $clone = $hideContent.closest('.pageNavLinkGroup').clone();
- $clone.find('.DisplayIgnoredContent').remove()
- if (!$clone.find('.linkGroup').children().length) $('.pageNavLinkGroup:visible').remove();
- }
- };
- if ($('html').hasClass('Public')) {
- $(function () {
- $('body').delegate('a.DisplayIgnoredContent', 'click', XenForo.DisplayIgnoredContent);
- if (window.location.hash) {
- var $jump = $(window.location.hash.replace(/[^\w_#-]/g, ''));
- if ($jump.hasClass('ignored')) {
- $jump.removeClass('ignored');
- $jump.get(0).scrollIntoView(true);
- }
- }
- });
- }
- // *********************************************************************
- XenForo.SpoilerBBCode = function ($spoiler) {
- $spoiler.click(function (e) {
- $spoiler.siblings(':first').css('fontSize', '25pt');
- return false;
- });
- /*$spoiler.click(function(e)
- {
- $spoiler.html($spoiler.data('spoiler')).removeClass('Spoiler').addClass('bbCodeSpoiler');
- });*/
- };
- // *********************************************************************
- /**
- * Produces centered, square crops of thumbnails identified by data-thumb-selector within the $container.
- * Requires that CSS of this kind is in place:
- * .SquareThumb
- * {
- * position: relative; display: block; overflow: hidden;
- * width: {$thumbHeight}px; height: {$thumbHeight}px;
- * }
- * .SquareThumb img
- * {
- * position: relative; display: block;
- * }
- *
- * @param jQuery $container
- */
- XenForo.SquareThumbs = function ($container) {
- var thumbHeight = $container.data('thumb-height') || 44,
- thumbSelector = $container.data('thumb-selector') || 'a.SquareThumb';
- console.info('XenForo.SquareThumbs: %o', $container);
- var $imgs = $container.find(thumbSelector).addClass('SquareThumb').children('img');
- var thumbProcessor = function () {
- var $thumb = $(this),
- w = $thumb.width(),
- h = $thumb.height();
- if (!w || !h) {
- return;
- }
- if (h > w) {
- $thumb.css('width', thumbHeight);
- $thumb.css('top', ($thumb.height() - thumbHeight) / 2 * -1);
- } else {
- $thumb.css('height', thumbHeight);
- $thumb.css('left', ($thumb.width() - thumbHeight) / 2 * -1);
- }
- };
- $imgs.load(thumbProcessor);
- $imgs.each(thumbProcessor);
- };
- // *********************************************************************
- // Register overlay-loading controls
- // TODO: when we have a global click handler, change this to use rel="Overlay" instead of class="OverlayTrigger"
- XenForo.register(
- 'a.OverlayTrigger, input.OverlayTrigger, button.OverlayTrigger, label.OverlayTrigger, a.username, a.avatar',
- 'XenForo.OverlayTrigger'
- );
- XenForo.register('.ToggleTrigger', 'XenForo.ToggleTrigger');
- // Register tooltip elements for desktop browsers
- XenForo.register('.Tooltip', 'XenForo.Tooltip');
- //XenForo.register('a.StatusTooltip', 'XenForo.StatusTooltip');
- XenForo.register('.PreviewTooltip', 'XenForo.PreviewTooltip');
- XenForo.register('a.LbTrigger', 'XenForo.LightBoxTrigger');
- // Register click-proxy controls
- XenForo.register('.ClickProxy', 'XenForo.ClickProxy');
- // Register popup menu controls
- XenForo.register('.Popup', 'XenForo.PopupMenu', 'XenForoActivatePopups');
- // Register scrolly pagenav elements
- XenForo.register('.PageNav', 'XenForo.PageNav');
- // Register tabs
- XenForo.register('.Tabs', 'XenForo.Tabs');
- // Register square thumb cropper
- XenForo.register('.SquareThumbs', 'XenForo.SquareThumbs');
- // Handle all xenForms
- XenForo.register('form.xenForm, .MultiSubmitFix', 'XenForo.MultiSubmitFix');
- // Register check-all controls
- XenForo.register('input.CheckAll, a.CheckAll, label.CheckAll', 'XenForo.CheckAll');
- // Register auto-checker controls
- XenForo.register('input.AutoChecker', 'XenForo.AutoChecker');
- // Register toggle buttons
- XenForo.register('label.ToggleButton', 'XenForo.ToggleButton');
- // Register auto inline uploader controls
- XenForo.register('form.AutoInlineUploader', 'XenForo.AutoInlineUploader');
- // Register form auto validators
- XenForo.register('form.AutoValidator', 'XenForo.AutoValidator');
- // Register auto time zone selector
- XenForo.register('select.AutoTimeZone', 'XenForo.AutoTimeZone');
- // Register generic content loader
- XenForo.register('a.Loader, input.Loader', 'XenForo.Loader');
- var supportsStep = 'step' in document.createElement('input');
- // Register form controls
- XenForo.register('input, textarea', function (i) {
- var $this = $(this);
- switch ($this.attr('type')) {
- case 'hidden':
- case 'submit':
- return;
- case 'checkbox':
- case 'radio':
- // Register auto submitters
- if ($this.hasClass('SubmitOnChange')) {
- XenForo.create('XenForo.SubmitOnChange', this);
- }
- return;
- }
- // Spinbox / input[type=number]
- if ($this.attr('type') == 'number' && supportsStep) {
- // use the XenForo implementation instead, as browser implementations seem to be universally horrible
- this.type = 'text';
- $this.addClass('SpinBox number');
- }
- if ($this.hasClass('SpinBox')) {
- XenForo.create('XenForo.SpinBox', this);
- }
- // Prompt / placeholder
- if ($this.hasClass('Prompt')) {
- console.error('input.Prompt[title] is now deprecated. Please replace any instances with input[placeholder] and remove the Prompt class.');
- $this.attr({ placeholder: $this.attr('title'), title: '' });
- }
- if ($this.attr('placeholder')) {
- XenForo.create('XenForo.Prompt', this);
- }
- // LiveTitle
- if ($this.data('livetitletemplate')) {
- XenForo.create('XenForo.LiveTitle', this);
- }
- // DatePicker
- if ($this.is(':date')) {
- XenForo.create('XenForo.DatePicker', this);
- }
- // AutoComplete
- if ($this.hasClass('AutoComplete')) {
- XenForo.create('XenForo.AutoComplete', this);
- }
- // UserTagger
- if ($this.hasClass('UserTagger')) {
- XenForo.create('XenForo.UserTagger', this);
- }
- // AutoSelect
- if ($this.hasClass('AutoSelect')) {
- XenForo.create('XenForo.AutoSelect', this);
- }
- // AutoValidator
- if (XenForo.isAutoValidatorField(this)) {
- XenForo.create('XenForo.AutoValidatorControl', this);
- }
- if ($this.is('textarea.StatusEditor')) {
- XenForo.create('XenForo.StatusEditor', this);
- }
- // Register Elastic textareas
- if ($this.is('textarea.Elastic')) {
- XenForo.create('XenForo.TextareaElastic', this);
- }
- });
- // Register form previewer
- XenForo.register('form.Preview', 'XenForo.PreviewForm');
- // Register field adder
- XenForo.register('a.FieldAdder, input.FieldAdder', 'XenForo.FieldAdder');
- // Read status toggler
- XenForo.register('a.ReadToggle', 'XenForo.ReadToggle');
- XenForo.StarContent = function ($link) {
- var callback = function (ajaxData) {
- if (!XenForo.hasResponseError(ajaxData)) {
- if ($link.hasClass('mainc')) {
- $link.removeClass('mainc');
- $link.attr('title', XenForo.phrases.addFave)
- } else {
- $link.addClass('mainc');
- $link.attr('title', XenForo.phrases.removeFave)
- }
- }
- };
- $link.on('click', function (e) {
- e.preventDefault();
- XenForo.ajax($link.attr('href'), {}, callback);
- });
- };
- XenForo.MarketBalanceSwitcher = function ($button) {
- this.__construct($button);
- };
- XenForo.MarketBalanceSwitcher.prototype =
- {
- __construct: function ($button) {
- this.$button = $button;
- this.showButtonFromCache();
- this.$button.on('click', $.context(this, 'switch'));
- },
- switch: function (e) {
- e.preventDefault();
- e.stopPropagation();
- XenForo.showMarketBalanceEnabled = !XenForo.showMarketBalanceEnabled;
- if (this.$button.hasClass('Enabled')) {
- localStorage.removeItem('market_show_balance_enabled');
- } else {
- localStorage.setItem('market_show_balance_enabled', '1');
- }
- this.$button.toggleClass('Enabled');
- $('#NavigationAccountUsername').toggleClass('hidden');
- $('#NavigationAccountBalance').toggleClass('hidden');
- },
- showButtonFromCache: function () {
- XenForo.showMarketBalanceEnabled = localStorage.hasOwnProperty('market_show_balance_enabled');
- if (XenForo.showMarketBalanceEnabled) {
- this.$button.addClass('Enabled');
- $('#NavigationAccountUsername').addClass('hidden');
- $('#NavigationAccountBalance').removeClass('hidden');
- }
- }
- };
- XenForo.Modal = function ($content, options) {
- this.__construct($content, options)
- }
- XenForo.Modal.prototype = {
- __construct: function ($content, options) {
- this.opened = false
- this.$modal = $('<div class="modal fade" />').append($content)
- if (!options) options = {}
- if (options.top && options.top === 'center') {
- $content.css('align-self', 'center')
- } else if (options.top) {
- $content.css('top', options.top)
- }
- if (options.close)
- this.$closeElems = this.$modal.find(options.close).on('click', $.proxy(this, 'hide'))
- this.$trigger = options.trigger || $($content)
- var self = this
- this.$modal.modal({
- show: false,
- zIndex: options.zIndex,
- severalModals: options.severalModals,
- preventClose: this.$trigger.data('unclosable') === true,
- unloadAlert: this.$trigger.data('alertonclose') === true
- }).bind({
- 'show.bs.modal': function () {
- self.opened = true
- self.$trigger.trigger('onBeforeLoad')
- options.onBeforeLoad && options.onBeforeLoad()
- },
- 'shown.bs.modal': function () {
- self.$trigger.trigger('onLoad')
- options.onLoad && options.onLoad()
- },
- 'hide.bs.modal': function () {
- self.opened = false
- self.$trigger.trigger('onBeforeClose')
- options.onBeforeClose && options.onBeforeClose()
- },
- 'hidden.bs.modal': function () {
- self.$trigger.trigger('onClose')
- options.onClose && options.onClose()
- }
- })
- var overlayData = { // длÃвЂГ‚ВЏ обÃвЂГўвЂљВ¬ГѓВђГ‚°ÃвЂГўв‚¬ЕЎГѓВђГ‚½ÃÂ¾ÃÂ¹ ГѓвЂГ‚ÃÂ¾ÃÂ²ÃÂ¼ÃÂµÃвЂГ‚ВЃГѓвЂГўв‚¬ЕЎГѓВђГ‚¸ÃÂ¼ÃÂ¾ÃвЂГ‚ВЃГѓвЂГўв‚¬ЕЎГѓВђГ‚Вё
- load: function () { self.show(); return overlayData },
- close: function () { self.hide(); return overlayData },
- destroy: function () { self.destroy(); return overlayData },
- getOverlay: function () { return $content },
- getConf: function () { return options },
- getTrigger: function () { return self.$trigger },
- getClosers: function () { return self.$closeElems ? this.$closeElems : $() },
- isOpened: function () { return self.opened }
- }
- this.$trigger.data('overlay', overlayData)
- },
- show: function () { this.$modal.modal('show'); },
- load: function () { this.show() },
- destroy: function () { this.$modal.remove() },
- hide: function () { this.$modal.modal('hide') }
- }
- XenForo.TimedMessage = function ($elem, timeout, callback) {
- this.__construct($elem, timeout, callback)
- }
- XenForo.TimedMessage.prototype = {
- __construct($elem, timeout, callback) {
- this.closed = false
- this.$elem = $elem instanceof $ ? $elem : $($elem)
- this.callback = callback
- $('body').append(this.$elem)
- this.$elem.css({
- top: 0,
- left: 0,
- position: 'fixed',
- display: 'block',
- zIndex: 15000,
- }).on('click', $.proxy(this, 'close'))
- setTimeout($.proxy(this, 'close'), timeout)
- },
- close() {
- if (this.closed) return
- this.closed = true
- this.callback && this.callback()
- this.$elem.animate({ opacity: 0 }, 500, function () {
- $(this).remove()
- })
- }
- }
- /**
- * Public-only registrations
- */
- if ($('html').hasClass('Public')) {
- XenForo.register('.StarContent', 'XenForo.StarContent');
- // Register the login bar handle
- //XenForo.register('#loginBar', 'XenForo.LoginBar');
- // Register the header search box
- XenForo.register('.QuickSearch', 'XenForo.QuickSearch');
- // Register attribution links
- XenForo.register('a.AttributionLink', 'XenForo.AttributionLink');
- // CAPTCHAS
- XenForo.register('#ReCaptcha', 'XenForo.ReCaptcha');
- XenForo.register('.NoCaptcha', 'XenForo.NoCaptcha');
- XenForo.register('#SolveMediaCaptcha', 'XenForo.SolveMediaCaptcha');
- XenForo.register('#KeyCaptcha', 'XenForo.KeyCaptcha');
- XenForo.register('#Captcha', 'XenForo.Captcha');
- // Resize large BB code images
- XenForo.register('img.bbCodeImage', 'XenForo.BbCodeImage');
- // Handle like/unlike links
- XenForo.register('a.LikeLink', 'XenForo.LikeLink');
- // Register node description tooltips
- if (!XenForo.isTouchBrowser()) {
- //XenForo.register('h3.nodeTitle a', 'XenForo.NodeDescriptionTooltip');
- }
- // Register visitor menu
- //XenForo.register('#AccountMenu', 'XenForo.AccountMenu');
- XenForo.register('#MarketBalanceSwitcher', 'XenForo.MarketBalanceSwitcher');
- // Register follow / unfollow links
- XenForo.register('a.FollowLink', 'XenForo.FollowLink');
- XenForo.register('li.PopupItemLink', 'XenForo.PopupItemLink');
- // Register notices
- XenForo.register('.Notices', 'XenForo.Notices');
- // Spoiler BB Code
- XenForo.register('button.Spoiler', 'XenForo.SpoilerBBCode');
- }
- // Register control disablers last so they disable anything added by other behaviours
- XenForo.register('input:checkbox.Disabler, input:radio.Disabler', 'XenForo.Disabler');
- // *********************************************************************
- var isScrolled = false;
- $(window).on('load', function () {
- if (isScrolled || !window.location.hash) {
- return;
- }
- var hash = window.location.hash.replace(/[^a-zA-Z0-9_-]/g, ''),
- $match = hash ? $('#' + hash) : $();
- if ($match.length) {
- $match.get(0).scrollIntoView(true);
- }
- });
- /**
- * Use jQuery to initialize the system
- */
- $(function () {
- XenForo.init();
- if (window.location.hash) {
- // do this after the document is ready as triggering it too early
- // causes the initial hash to trigger a scroll
- $(window).one('scroll', function (e) {
- isScrolled = true;
- });
- }
- var scrollTrigger = 100; // px
- var backToTop = function () {
- var scrollTop = $(window).scrollTop();
- if (scrollTop > scrollTrigger) {
- $('.cd-top').addClass('cd-is-visible');
- } else {
- $('.cd-top').removeClass('cd-is-visible');
- }
- };
- $(window).on('load scroll', function () {
- backToTop();
- });
- $('.cd-top').on('click', function (e) {
- e.preventDefault();
- $('html,body').animate({
- scrollTop: 0
- }, 350);
- });
- $('a[href^="#"]').on('click', function (e) {
- if (!$(this).hasClass('cd-top') && $(this.hash).offset()) {
- e.preventDefault();
- $('html,body').animate({ scrollTop: ($(this.hash).offset().top - 44) }, 500);
- }
- });
- })
- }
- (jQuery, this, document);
- +function ($) {
- 'use strict';
- // MODAL CLASS DEFINITION
- // ======================
- var Modal = function (element, options) {
- this.options = options
- this.$body = $(document.body)
- this.$html = $('html')
- this.$element = $(element)
- this.$dialog = this.$element.find('.modal-dialog')
- this.$backdrop = null
- this.isShown = null
- this.originalBodyPad = null
- this.scrollbarWidth = 0
- this.ignoreBackdropClick = false
- this.fixedContent = ''; //'.navbar-fixed-top, .navbar-fixed-bottom, #navigation'
- this.zIndex = options.zIndex || 0
- this.zIndexOffset = 10001
- if (this.options.remote) {
- this.$element
- .find('.modal-content')
- .load(this.options.remote, $.proxy(function () {
- this.$element.trigger('loaded.bs.modal')
- }, this))
- }
- this.$element
- .css('z-index', this.zIndexOffset + 1 + this.zIndex * 2)
- .attr('data-z-index', this.zIndex)
- }
- Modal.VERSION = '3.4.1'
- Modal.TRANSITION_DURATION = 200
- Modal.BACKDROP_TRANSITION_DURATION = 150
- Modal.DEFAULTS = {
- backdrop: true,
- keyboard: true,
- show: true
- }
- Modal.prototype.toggle = function (_relatedTarget) {
- return this.isShown ? this.hide() : this.show(_relatedTarget)
- }
- Modal.prototype.show = function (_relatedTarget) {
- var $modal = $('.modal.in[data-z-index=' + this.zIndex + ']')
- if (this.options.severalModals) {
- this.showModal(_relatedTarget);
- var $fade = $('div.modal-backdrop.fade.in').last();
- $fade.css('z-index', parseInt($fade.css('z-index')) + 5);
- this.$element
- .css('z-index', parseInt($fade.css('z-index')) + 6)
- .attr('data-z-index', parseInt($fade.css('z-index')) + 6)
- return;
- }
- if ($modal.length) {
- $modal.data('bs.modal').hide(null, function () {
- this.showModal(_relatedTarget)
- }.bind(this))
- } else this.showModal(_relatedTarget)
- }
- Modal.prototype.showModal = function (_relatedTarget) {
- var that = this
- if (this.isShown) return
- this.isShown = true
- this.checkScrollbar()
- this.setScrollbar()
- this.$html.addClass('modal-open')
- if (this.options.unloadAlert) {
- window.onbeforeunload = function () {
- return true;
- };
- }
- if (!this.options.preventClose) this.escape()
- this.resize()
- //this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
- this.$dialog.on('mousedown.dismiss.bs.modal', function () {
- that.$element.one('mouseup.dismiss.bs.modal', function (e) {
- if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
- })
- })
- if (!this.$element.parent().length) {
- this.$element.appendTo(that.$body) // don't move modals dom position
- }
- var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
- this.$element.trigger(e)
- this.backdrop(function () {
- var transition = that.$element.hasClass('fade')
- that.$element
- .show()
- .scrollTop(0)
- that.adjustDialog()
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
- that.$element.addClass('in')
- that.enforceFocus()
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
- that.$element.find('.overlayScroll').addClass('scrollbar-macosx scrollbar-dynamic').scrollbar()
- that.$element.attr('tabindex', '-1').css('outline', 'none')
- transition ?
- setTimeout(function () {
- that.$element.trigger('focus').trigger(e)
- }, Modal.TRANSITION_DURATION) :
- that.$element.trigger('focus').trigger(e)
- })
- }
- Modal.prototype.hide = function (e, cb) {
- if (e) e.preventDefault()
- e = $.Event('hide.bs.modal')
- this.$element.trigger(e)
- if (!this.isShown || e.isDefaultPrevented()) return
- this.isShown = false
- this.escape()
- this.resize()
- window.onbeforeunload = null;
- $(document).off('focusin.bs.modal')
- this.$element
- .removeClass('in')
- .off('click.dismiss.bs.modal')
- .off('mouseup.dismiss.bs.modal')
- this.$dialog.off('mousedown.dismiss.bs.modal')
- setTimeout(function () {
- this.hideModal(cb)
- }.bind(this), Modal.TRANSITION_DURATION)
- }
- Modal.prototype.enforceFocus = function () {
- $(document)
- .off('focusin.bs.modal') // guard against infinite focus loop
- .on('focusin.bs.modal', $.proxy(function (e) {
- if (document !== e.target &&
- this.$element[0] !== e.target &&
- !this.$element.has(e.target).length) {
- this.$element.trigger('focus')
- }
- }, this))
- }
- Modal.prototype.escape = function () {
- if (this.isShown && this.options.keyboard) {
- this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
- e.which == 27 && this.hide()
- }, this))
- } else if (!this.isShown) {
- this.$element.off('keydown.dismiss.bs.modal')
- }
- }
- Modal.prototype.resize = function () {
- if (this.isShown) {
- $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
- } else {
- $(window).off('resize.bs.modal')
- }
- }
- Modal.prototype.hideModal = function (cb) {
- var that = this
- this.$element.hide()
- this.backdrop(function () {
- if (!$('.modal.in:visible').length)
- that.$html.removeClass('modal-open')
- that.resetAdjustments()
- that.resetScrollbar()
- that.$element.trigger('hidden.bs.modal')
- let $modal = $('.modal.in:visible');
- if ($modal.length) {
- $modal.attr('tabindex', '-1')
- $modal.trigger('focus')
- }
- $('.modal.in:hidden').removeClass('in')
- if (cb) cb()
- })
- }
- Modal.prototype.removeBackdrop = function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
- }
- Modal.prototype.backdrop = function (callback) {
- var that = this
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
- if (this.isShown && this.options.backdrop) {
- var doAnimate = animate
- this.$backdrop = $(document.createElement('div'))
- .addClass('modal-backdrop ' + animate)
- .appendTo(this.$body)
- .css('z-index', this.zIndexOffset + this.zIndex * 2)
- if (!this.options.preventClose) {
- this.$element.on('mousedown.dismiss.bs.modal touchstart.dismiss.bs.modal', $.proxy(function (e) {
- if (this.ignoreBackdropClick) {
- this.ignoreBackdropClick = false
- return
- }
- if (e.target !== e.currentTarget) return
- if (this.$element[0].scrollHeight > document.documentElement.clientHeight && e.clientX > this.$backdrop.width() - this.scrollbarWidth)
- return
- this.options.backdrop == 'static'
- ? this.$element[0].focus()
- : this.hide()
- }, this))
- }
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
- this.$backdrop.addClass('in')
- if (!callback) return
- if (!doAnimate) return callback()
- setTimeout(function () {
- callback()
- }.bind(this), Modal.BACKDROP_TRANSITION_DURATION)
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
- var callbackRemove = function () {
- that.removeBackdrop()
- callback && callback()
- }
- if (this.$element.hasClass('fade'))
- setTimeout(function () {
- callbackRemove()
- }.bind(this), Modal.BACKDROP_TRANSITION_DURATION)
- } else if (callback) {
- callback()
- }
- }
- // these following methods are used to handle overflowing modals
- Modal.prototype.handleUpdate = function () {
- this.adjustDialog()
- }
- Modal.prototype.adjustDialog = function () {
- var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
- this.$element.css({
- paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
- paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
- })
- }
- Modal.prototype.resetAdjustments = function () {
- this.$element.css({
- paddingLeft: '',
- paddingRight: ''
- })
- }
- Modal.prototype.checkScrollbar = function () {
- var fullWindowWidth = window.innerWidth
- if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
- var documentElementRect = document.documentElement.getBoundingClientRect()
- fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
- }
- this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
- this.scrollbarWidth = this.measureScrollbar()
- }
- Modal.prototype.setScrollbar = function () {
- if ($('.modal.in:visible').length) return;
- var bodyPad = parseInt((this.$html.css('padding-right') || 0), 10)
- this.originalBodyPad = document.body.style.paddingRight || ''
- var scrollbarWidth = this.scrollbarWidth
- if (this.bodyIsOverflowing) {
- this.$html.css('padding-right', bodyPad + scrollbarWidth)
- $(this.fixedContent).each(function (index, element) {
- var actualPadding = element.style.paddingRight
- var calculatedPadding = $(element).css('padding-right')
- $(element)
- .data('padding-right', actualPadding)
- //.css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px')
- })
- if ($('#ChatboxStartIcon').length)
- $('#ChatboxStartIcon')
- .css('transition', 'none')
- .css('right', (15 + scrollbarWidth) + 'px')
- if ($('#completeChatbox').length)
- $('#completeChatbox')
- .data('right', $('#completeChatbox').css('right'))
- .css('right', (parseInt($('#completeChatbox').css('right')) + scrollbarWidth) + 'px')
- }
- }
- Modal.prototype.resetScrollbar = function () {
- if ($('.modal.in:visible').length) return;
- this.$html.css('padding-right', this.originalBodyPad)
- $(this.fixedContent).each(function (index, element) {
- var padding = $(element).data('padding-right')
- $(element).removeData('padding-right')
- element.style.paddingRight = padding ? padding : ''
- })
- if ($('#ChatboxStartIcon').length) {
- $('#ChatboxStartIcon').css('right', '15px')
- setTimeout(function () {
- $('#ChatboxStartIcon').css('transition', '.2s')
- }, 250)
- }
- if ($('#completeChatbox').length)
- $('#completeChatbox').css('right', $('#completeChatbox').data('right'))
- }
- Modal.prototype.measureScrollbar = function () {
- return window.innerWidth - document.documentElement.getBoundingClientRect().width
- }
- // MODAL PLUGIN DEFINITION
- // =======================
- function Plugin(option, _relatedTarget) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.modal')
- var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
- if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option](_relatedTarget)
- else if (options.show) data.show(_relatedTarget)
- })
- }
- var old = $.fn.modal
- $.fn.modal = Plugin
- $.fn.modal.Constructor = Modal
- // MODAL NO CONFLICT
- // =================
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
- }
- // MODAL DATA-API
- // ==============
- $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
- var $this = $(this)
- var href = $this.attr('href')
- var target = $this.attr('data-target') ||
- (href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
- var $target = $(document).find(target)
- var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
- if ($this.is('a')) e.preventDefault()
- $target.one('show.bs.modal', function (showEvent) {
- if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
- $target.one('hidden.bs.modal', function () {
- $this.is(':visible') && $this.trigger('focus')
- })
- })
- Plugin.call($target, option, this)
- })
- XenForo.SmiliesPicker = function ($element) {
- this.__construct($element);
- };
- XenForo.SmiliesPicker.prototype =
- {
- __construct: function ($element) {
- console.log("[Smilies] Loading smilies for", $element)
- this.$element = $element;
- if (this.$element.get(0)._tippy !== undefined) {
- this.$element.get(0)._tippy.destroy()
- }
- this.$element.on('mousedown', function (e) {
- e.preventDefault()
- })
- this.$element.off('click')
- this.$smilies = undefined;
- this.localStorageKey = XenForo.smiliesCacheKey;
- this.smiliesLoaded = false;
- this.recentlyUsedLimit = 14;
- let ownerDocument = document.implementation.createHTMLDocument('virtual'); // Used to edit img tags without loading them
- if (supports_html5_storage()) {
- let temp = localStorage.getItem(this.localStorageKey);
- if (temp && temp.startsWith("<")) {
- this.cleanSmiliesCache();
- this.smiliesBox = $(temp, ownerDocument);
- this.smiliesBox.find('img').map(function () {
- $(this).attr("data-src", $(this).attr("src"));
- $(this).removeAttr('src');
- $(this).css({ 'display': 'none' });
- })
- this.createSmiliesBox();
- this.smiliesLoaded = true;
- }
- }
- if (!this.smiliesLoaded) {
- XenForo.ajax(
- XenForo._editorSmiliesUrl,
- {},
- function (ajaxData) {
- if (XenForo.hasResponseError(ajaxData)) {
- return;
- }
- if (ajaxData.templateHtml) {
- new XenForo.ExtLoader(ajaxData, function () {
- let recentlyUsedSmilies = this.getRecentlyUsedSmiliesFromCache();
- this.cleanSmiliesCache();
- this.smiliesBox = $(ajaxData.templateHtml, ownerDocument);
- this.smiliesBox.find('img').map(function () {
- $(this).attr("data-src", $(this).attr("src"));
- $(this).removeAttr('src');
- $(this).css({ 'display': 'none' });
- })
- if (recentlyUsedSmilies) {
- this.smiliesBox.find('.RecentlyUsedSmilies').html(recentlyUsedSmilies).removeClass('hidden');
- }
- if (supports_html5_storage()) {
- try {
- localStorage.setItem(this.localStorageKey, this.smiliesBox[0].outerHTML);
- } catch (e) {
- this.clearLocalStorage(256);
- localStorage.setItem(this.localStorageKey, this.smiliesBox[0].outerHTML);
- }
- }
- this.createSmiliesBox()
- }.bind(this));
- }
- }.bind(this)
- );
- }
- },
- cleanSmiliesCache: function () {
- let values = Object.keys(localStorage)
- .filter((key) => key !== this.localStorageKey && !isNaN(+new Date(parseInt(key) * 1000)) && localStorage.getItem(key).indexOf('<li class="Smilie') !== -1);
- values.forEach(function (item, i, arr) {
- localStorage.removeItem(item);
- });
- },
- locateSmiliesElement: function () {
- if (this.$element.closest('.input').length) {
- return this.$element.closest('.input').find('#smiliesBox');
- } else if (this.$element.closest('.commentSubmit').length) {
- if (!this.$element.closest('.commentSubmit').find('.redactor_smilies').length) {
- this.$element.closest('.commentSubmit').prepend($('<div class="redactor_smilies" />'));
- }
- return this.$element.closest('.commentSubmit').find('.redactor_smilies');
- } else if (this.$element.closest('.redactor_box').length) {
- if (!this.$element.closest('.redactor_box').parent().find('.redactor_smilies').length) {
- this.$element.closest('.redactor_box').parent().prepend($('<div class="redactor_smilies" />'));
- }
- return this.$element.closest('.redactor_box').parent().find('.redactor_smilies');
- } else if (this.$element.closest('form').length) {
- if (!this.$element.closest('form').find('.redactor_smilies').length) {
- this.$element.closest('form').prepend($('<div class="redactor_smilies" />'));
- }
- return this.$element.closest('form').find('.redactor_smilies');
- }
- },
- locateInputElement: function () {
- if (this.$element.parent().find('input[type=text]').length) {
- return this.$element.parent().find('input[type=text]');
- } else if (this.$element.closest('.commentSubmit').length) {
- return this.$element.closest('.commentSubmit');
- } else if (this.$element.closest('.redactor_box').length) {
- return this.$element.closest('.redactor_box').parent();
- } else if (this.$element.closest('form').length) {
- return this.$element.closest('form');
- }
- },
- setContent: function (content) {
- if (XenForo.isTouchBrowser()) {
- this.$smilies.html(content.children()).xfActivate()
- } else {
- this.$element.get(0)._tippy.setContent(content)
- }
- this.bind();
- },
- getRecentlyUsedSmiliesFromCache: function () {
- let values = Object.keys(localStorage)
- .filter((key) => key !== this.localStorageKey && !isNaN(+new Date(parseInt(key) * 1000)) && localStorage.getItem(key).indexOf('<li class="Smilie') !== -1);
- let returnValue = null;
- values.forEach(function (item, i, arr) {
- returnValue = $(localStorage.getItem(item)).find('.RecentlyUsedSmilies').html();
- });
- return returnValue;
- },
- addSmilieToRecentlyUsed: function ($smilie) {
- let selector = '.' + $smilie.attr('class').replace(' ', '.');
- let $cachedSmilie = this.$smilies.find('.RecentlyUsedSmilies').find(selector);
- if ($cachedSmilie.length) {
- $cachedSmilie.remove();
- }
- let $clone = $smilie.clone();
- $clone.xfInsert('prependTo', this.$smilies.find('.RecentlyUsedSmilies').find('ul'), 'show', 0, function () {
- $clone.css('display', '');
- this.$smilies.find('.RecentlyUsedSmilies').removeClass('hidden');
- this.$smilies.find('.RecentlyUsedSmilies .Smilie').off('click').slice(this.recentlyUsedLimit).remove();
- if (supports_html5_storage()) {
- this.$save = this.$smilies.clone()
- this.$save.find('img').map(function () {
- $(this).attr("data-src", $(this).attr("src"));
- $(this).removeAttr('src');
- $(this).css({ 'display': 'none' });
- })
- try {
- localStorage.setItem(this.localStorageKey, this.$save.html());
- } catch (e) {
- this.clearLocalStorage(256);
- localStorage.setItem(this.localStorageKey, this.$save.html());
- }
- }
- }.bind(this));
- },
- appendToInput: function ($smilie) {
- let $input = this.locateInputElement()
- if ($input.is("input")) {
- $input.val($input.val() + $smilie.find('img').attr('alt')).focus();
- } else {
- let $ed = XenForo.getEditorInForm($input);
- $ed.execCommand('inserthtml', $smilie.html().trim());
- $ed.focus();
- }
- },
- createSmiliesBox: function () {
- if (XenForo.isTouchBrowser()) {
- this.loadMobile();
- } else {
- this.loadDesktop();
- }
- },
- updateSmiliesKits: function ($el) {
- $el.addClass('updated-smilies')
- const $nav = $('<div class="smilies-nav"><div class="smilies-nav-container"></div></div>')
- $nav.find('.smilies-nav-container')
- .append($el.find('.navigation').children().clone())
- $el.find('.navigation').css('display', 'none');
- $el.append($nav)
- },
- loadDesktop: function () {
- this.smiliesBox.find('img').map(function () {
- $(this).css({ 'display': 'none' });
- })
- let content = this.smiliesBox[0].outerHTML
- let d = document.createElement('div');
- let contentSelector = 'chatboxSmilies';
- $(d).attr('id', contentSelector).addClass('simpleRedactor--smiliesBox').hide();
- $(d).html(content).show();
- this.updateSmiliesKits($(d))
- this.$smilies = $(d);
- let isFirstShown = true;
- let $content = $('<div>').append(this.$smilies.clone());
- this._tippy = tippy(this.$element.get(0), {
- content: $content.html(),
- theme: 'popup',
- interactive: true,
- arrow: true,
- animation: 'shift-toward',
- hideOnClick: false,
- zIndex: this.$element.closest('.xenOverlay').length || this.$element.closest('.StackAlerts').length ? 11111 : 9999,
- onHidden: function () {
- this.$smilies.find('ul.smilieContainer').get(0).scrollTop = 0;
- }.bind(this),
- onShown: function (element) {
- this.$smilies = $(element.popper).find('#chatboxSmilies');
- // If element position is over real page size
- var picker = $('.tippy-popper')[0].getBoundingClientRect()
- if (window.innerWidth < (picker.x + picker.width)) {
- var amount = picker.x + picker.width - window.innerWidth + 20
- $('.tippy-popper').css('margin-left', '-' + amount + 'px').css('margin-right', amount + 'px')
- } else {
- if (window.innerWidth > (picker.x + picker.width + parseInt($('.tippy-popper').css('margin-right'), 10))) {
- $('.tippy-popper').css('margin-left', '0px').css('margin-right', '0px')
- }
- }
- if (!isFirstShown) return;
- // Lazy loading
- $content.find('img').map(function () {
- $(this).attr('src', $(this).data("src"));
- $(this).css({ 'display': '' });
- })
- this.$element.get(0)._tippy.setContent($content.html());
- this.$smilies = $(element.popper).find('#chatboxSmilies');
- $('.smilies-nav-container')
- .addClass('scrollbar-macosx scrollbar-dynamic').scrollbar()
- .siblings('.scroll-x').find('.scroll-bar').css('left', 0);
- this.bind()
- isFirstShown = false;
- }.bind(this)
- });
- let $input = this.locateInputElement()
- if (!$input.is("input")) {
- $input = XenForo.getEditorInForm($input).$editor;
- if (!$input) {
- return
- }
- }
- $input.on('keydown', function (e) {
- if (e.keyCode === 9 && !!this._tippy) {
- e.preventDefault();
- if (this._tippy.state.isShown) {
- this._tippy.hide()
- } else {
- this._tippy.show()
- }
- }
- }.bind(this))
- },
- slideSmilies: function () {
- this.$smilies.slideToggle();
- const scrollElement = $('body,html')
- const scrollTop = $(".smilieContainer").offset().top - 200
- if (Math.abs(scrollElement.scrollTop() - scrollTop) > 100) {
- scrollElement.animate({ scrollTop }, { duration: 450, queue: false });
- }
- },
- loadMobile: function () {
- this.$element.off('click').click(function (e) {
- e.preventDefault();
- e.stopImmediatePropagation();
- this.$smilies = this.locateSmiliesElement();
- if (!this.$smilies.length) {
- return;
- }
- if (this.$smilies.children().length) {
- this.slideSmilies();
- return;
- }
- this.smiliesBox.find('img').map(function () {
- $(this).attr('src', $(this).data("src"));
- $(this).css({ 'display': '' });
- })
- this.$smilies.html(this.smiliesBox[0].outerHTML)
- this.$smilies.hide();
- this.bind();
- this.slideSmilies();
- }.bind(this));
- },
- bind: function () {
- this.$smilies.xfActivate();
- this.$smilies.off('click').on('click', '.Smilie', function (e) {
- let target = e.target.closest('li') || e.target;
- this.appendToInput($(target));
- if (!$(target).closest('.RecentlyUsedSmilies').length) {
- this.addSmilieToRecentlyUsed($(target));
- }
- const acResults = $('.autoCompleteListSmilies:visible')
- if (acResults && acResults.length) {
- acResults.data('XenForo.AutoSmiliesCompleteResults').hideResults()
- }
- return true;
- }.bind(this));
- const kitsPositions = []
- this.$smilies.find('.NavigationSmilie').each(function () {
- const elem = $($(this).data("id").replace("Id", ""))
- const parent = $("ul.smilieContainer")
- kitsPositions.push([elem.attr('id'), elem.offset().top - parent.offset().top])
- const self = $(this)
- self.off('click').on('click', function (e) {
- $('.smilies-nav-container li.active-kit').removeClass('active-kit')
- self.addClass('active-kit')
- parent.scrollTop(parent.scrollTop() + (elem.offset().top - parent.offset().top));
- });
- });
- if (!XenForo.isTouchBrowser()) {
- const navContainer = $(".smilies-nav-container")[1]
- navContainer.addEventListener("wheel", e => {
- e.preventDefault();
- navContainer.scrollLeft += (Math.abs(e.deltaY) > Math.abs(e.deltaX) ? e.deltaY : e.deltaX) * 0.5;
- });
- let prev_active_kit = 'smilieCategory--0'
- this.$smilies.find('.smilieContainer').off('scroll').on('scroll', function (e) {
- const scroll_pos = $(this).scrollTop()
- kitsPositions.some((item, index) => {
- if (scroll_pos < item[1]) {
- const active_kit = kitsPositions[index === 0 ? 0 : index - 1][0]
- if (active_kit !== prev_active_kit) {
- prev_active_kit = active_kit
- const parent = $('.smilies-nav-container')
- const elem = $('.smilies-nav-container li[data-id="#' + active_kit.replace('--', 'Id--') + '"]')
- $('.smilies-nav-container li.active-kit').removeClass('active-kit')
- elem.addClass('active-kit')
- parent.animate({
- scrollLeft: parent.scrollLeft() + (elem.offset().left - parent.offset().left)
- }, { duration: 200, queue: false });
- }
- return true
- }
- })
- })
- }
- },
- clearLocalStorage: function (counter) {
- if (this.getFreeLocalStorageSize(counter) <= 256) { // kB
- let values = Object.keys(localStorage)
- .filter((key) => key.startsWith('market_search_bar'));
- for (let i = 0; i < values.length; i++) {
- localStorage.removeItem(values[i])
- if (this.getFreeLocalStorageSize(counter) >= 512) {
- return
- }
- }
- }
- },
- getFreeLocalStorageSize: function (counter) {
- localStorage.removeItem('XenForo');
- if (localStorage && !localStorage.getItem('XenForo')) {
- let i = 0;
- try {
- for (i = counter; i <= 10000; i += counter) {
- localStorage.setItem('XenForo', new Array((i * 1024) + 1).join('a'));
- }
- } catch (e) {
- localStorage.removeItem('XenForo');
- return i - counter;
- }
- }
- },
- };
- XenForo.DisableButton = function ($el) {
- var $spinner = $('<i class="fa fa-spinner fa-spin"></i>').hide().appendTo($el)
- var loading = false
- $el.on('click', function (e) {
- if (loading) e.preventDefault()
- })
- $el.on('BeforeOverlayTrigger', function (e) {
- if (loading) e.preventDefault()
- loading = true
- $spinner.show()
- $el.addClass('disabled')
- })
- $(document).on('OverlayOpening', function () {
- if (!loading) return;
- loading = false
- $spinner.hide()
- $el.removeClass('disabled')
- })
- }
- XenForo.register('.DisableButton', 'XenForo.DisableButton')
- XenForo.register('.smiliePicker', 'XenForo.SmiliesPicker');
- XenForo.register('.SmiliesTooltip', 'XenForo.SmiliesPicker');
- XenForo.register('.redactor_btn_smilies', 'XenForo.SmiliesPicker');
- XenForo.register('.redactor_btn_container_smilies', 'XenForo.SmiliesPicker');
- XenForo.register('span.ImNotification--Smiliepicker', 'XenForo.SmiliesPicker');
- // *********************************************************************
- XenForo.ReportForm = function ($element) {
- $element.find('input[type="radio"]').labelauty();
- $element.find('.reportReasons > label').on("click", function (e) {
- e.stopPropagation();
- e.stopImmediatePropagation();
- e.preventDefault();
- $element.find('._insertHereReason').val($(this).find('.labelauty-checked').text());
- $element.find('input[name="is_common_reason"]').val("1");
- $element.trigger('submit');
- });
- };
- XenForo.register('._reportForm', 'XenForo.ReportForm');
- // *********************************************************************
- XenForo.SuggestThreads = function ($selector) {
- XenForo.scriptLoader.loadCss(['discussion_list'], "css.php?css=__sentinel__&style=9&_v=" + XenForo._jsVersion, function () {
- this.__construct($selector);
- }.bind(this), function () { });
- };
- XenForo.SuggestThreads.prototype = {
- __construct: function ($select) {
- if (!$select.hasClass('SuggestThreads')) return;
- this.$input = $select;
- this.$element = $select.closest('.titleUnit');
- let $fieldset = $select.closest('fieldset');
- $fieldset.find('dl').each((_, element) => {
- if (!$(element).hasClass('fullWidth'))
- $fieldset.before($(element));
- })
- $fieldset.parent().xfActivate();
- let timer;
- $('<div class="discussionList" style="padding: 0px !important;"><div class="latestThreads _insertLoadedContent"/></div>').appendTo(this.$element);
- this.$input.on('keyup', function () {
- clearTimeout(timer);
- timer = setTimeout(() => { this.hitEdit() }, 400);
- }.bind(this))
- },
- hitEdit: function () {
- let value = this.$input.val().trim();
- let counter = 0;
- if (!value) return $('._insertLoadedContent').css('padding-top', '0px').html('');
- XenForo.ajax(window.location.href.substring(0, window.location.href.lastIndexOf('/') + 1), { order: 'last_post_date', direction: 'desc', period: '', state: 'active', 'title': value }, function (ajaxData) {
- var $data = $(ajaxData.templateHtml).find('._insertLoadedContent');
- $('._insertLoadedContent').css('padding-top', '0px').html('');
- if (!$data.length) return;
- $('._insertLoadedContent').replaceWith($data).xfActivate();
- $(`<p class="textHeading">${XenForo.phrases.check_similar_threads}</p>`).prependTo($('._insertLoadedContent'));
- $('._insertLoadedContent').css('padding-top', '20px').find('.discussionListItem').each(function () {
- counter++;
- if (counter > 5) $(this).remove();
- })
- })
- }
- }
- XenForo.register('#ctrl_title_thread_create', 'XenForo.SuggestThreads');
- // *********************************************************************
- XenForo.PasswordInput = function ($element) {
- this.$element = $element;
- if (!this.$element.is(':visible')) return;
- this.$parent = $element.wrap('<div class="inputRelative"/>').parent();
- this.$button = $('<i class="inputRelativeIcon fas fa-eye"/>').appendTo(this.$parent);
- this.$button.on('click', function () {
- if (this.$element.attr('type') === 'password') {
- this.$button.removeClass('fa-eye').addClass('fa-eye-slash');
- this.$element.attr('type', 'text');
- } else {
- this.$button.removeClass('fa-eye-slash').addClass('fa-eye');
- this.$element.attr('type', 'password');
- }
- }.bind(this))
- };
- XenForo.register('input[type=password]', 'XenForo.PasswordInput');
- // *********************************************************************
- XenForo.EnhancedAutoComplete = function ($selector) {
- $selector.prop("disabled", true);
- XenForo.scriptLoader.loadCss(['select2'], "css.php?css=__sentinel__&style=9&_v=" + XenForo._jsVersion, function () {
- this.__construct($selector);
- }.bind(this), function () { });
- };
- XenForo.EnhancedAutoComplete.prototype = {
- __construct: function ($element) {
- this.$element = $element;
- this.$element.prop("disabled", false);
- this.uniqueName = "enhancedAutoComplete" + Math.floor(Math.random() * new Date());
- this.$parent = this.$element.wrap(`<div class="${this.uniqueName}"/>`).parent();
- this.$select = $(`<select multiple="multiple" class="textCtrl" data-placeholder="${this.$element.attr('placeholder') || ''}" aria-hidden="true"></select>`).appendTo(this.$parent);
- this.height = this.$element.height();
- this.$select.select2({
- escapeMarkup: function (val) {
- return DOMPurify.sanitize(val);
- },
- width: this.$element.width() + 20 + "px",
- maximumSelectionLength: this.$element.hasClass('AcSingle') ? 1 : 0,
- dropdownParent: $('<div/>'),
- sorter: function (sort) {
- this.bind()
- return sort;
- }.bind(this)
- });
- $(`<style>.${this.uniqueName} li.select2-selection__choice {
- height:${this.height - 10}px !important;
- line-height:${this.height - 10}px !important;
- }
- .${this.uniqueName} img.smallCompleteAvatar {
- width: ${this.height - 14}px;
- height: ${this.height - 14}px;
- }
- </style>`).appendTo(this.$parent)
- this.$element.css('display', 'none');
- this.$element.attr("class").split(/\s+/).map((val) => {
- this.$parent.find('.select2-selection__rendered').addClass(val)
- if (val !== "textCtrl") this.$parent.find('.select2').addClass(val)
- })
- this.$parent.find('.select2-selection__rendered').attr('style', 'height: inherit !important;')
- this.acResults = new XenForo.AutoCompleteResults({
- onInsert: $.context(this, 'insertAutoComplete')
- });
- this.$parent.find('.select2-search__field').addClass('AutoComplete').xfActivate();
- if (this.$element.val()) this.initializeOld(this.$element.val().split(","));
- this.bind();
- this.$select.on('change', function () {
- let elements = [];
- let data = this.$select.select2('data');
- for (const el in data) {
- if (data[el].selected) elements.push(data[el].id);
- }
- this.$element.val(elements.join(","));
- this.$element.trigger('change');
- setTimeout(() => { this.bind() })
- }.bind(this));
- if (this.$parent.find('.select2-search__field').css('width') && !this.$parent.find('.select2-search__field').attr('width')) {
- this.$parent.find('.select2-search__field').attr('width', 'set');
- let padding = parseInt(window.getComputedStyle(this.$parent.find('.select2-selection__rendered').get(0), null).getPropertyValue('padding-left'));
- if (this.$parent.find('.select2-search__field').width() - padding > 0) {
- this.$parent.find('.select2-search__field').css('width', this.$parent.find('.select2-search__field').width() - padding + 'px');
- }
- }
- if (this.$element.val() && this.$element.val().split(",").length === 1) {
- XenForo.ajax(XenForo.AutoComplete.getDefaultUrl(), { q: this.$element.val() }, function (ajaxData) {
- if (ajaxData.results !== []) {
- this.$element.data('result', ajaxData);
- this.initializeOld(this.$element.val().split(","));
- }
- }.bind(this))
- }
- },
- findMatch: function (match) {
- let resultKeys = Object.keys((this.$element.data('result') || {}).results || {});
- for (let i = 0; i < resultKeys.length; i++) {
- if (resultKeys[i].toLowerCase() === match.toLowerCase()) {
- return resultKeys[i]
- }
- }
- return false;
- },
- formatAvatar: function (result) {
- let $html = $(result.username)
- $html.find('.scamNotice').remove()
- let $avatar = $html.clone();
- $avatar.prepend($(`<img class="smallCompleteAvatar" src="${result.avatar}" style="float: left;margin-top: 2px;margin-right: 5px;border-radius: 17px"/>`))
- return $avatar.prop('outerHTML');
- },
- initializeOld: function (value) {
- let elements = [];
- for (let i = 0; i < value.length; i++) {
- value[i] = XenForo.htmlspecialchars(value[i])
- elements.push(value[i]);
- let match = this.findMatch(value[i]);
- let newOption = new Option(match ? this.formatAvatar(this.$element.data('result').results[match]) : value[i], value[i], false, false);
- let hit;
- this.$select.find('option').each(function () {
- if ($(this).attr('value') === value[i]) {
- if (match) $(this).remove()
- else hit = true
- }
- })
- if (!hit)
- this.$select.append(newOption).trigger('change');
- }
- this.$select.val(elements);
- this.$select.trigger('change');
- this.bind();
- },
- insertAutoComplete: function (name, html, avatar) {
- let elements = [];
- let data = this.$select.select2('data');
- for (let el = 0; el < data.length; el++) {
- if (data[el].selected) elements.push(data[el].id);
- }
- elements.push(name);
- if (!this.$select.find('option').filter((_, element) => { return $(element).attr('value') === avatar || $(element).attr('value') === name }).length) {
- let newOption = new Option(avatar, name, false, false);
- this.$select.append(newOption).trigger('change');
- }
- this.$select.val(elements);
- this.$select.trigger('change');
- this.$element.val(elements.join(","))
- this.lastAcLookup = '';
- this.$parent.find('.select2-search__field').val('');
- setTimeout(() => { this.bind() })
- },
- bind: function () {
- this.$parent.find('.select2-search__field').off('focus keydown keyup').on('focus keydown', function (e) {
- if (e.type === "focus" && this.$parent.find('.select2-search__field').css('width') && !this.$parent.find('.select2-search__field').attr('width')) {
- this.$parent.find('.select2-search__field').attr('width', 'set');
- let padding = parseInt(window.getComputedStyle(this.$parent.find('.select2-selection__rendered').get(0), null).getPropertyValue('padding-left'));
- if (this.$parent.find('.select2-search__field').width() - padding > 0) {
- this.$parent.find('.select2-search__field').css('width', this.$parent.find('.select2-search__field').width() - padding + 'px');
- }
- }
- var prevent = true,
- acResults = this.acResults;
- if (e.keyCode === 8 && this.$select.select2('data').length && !this.$parent.find('.select2-search__field').val()) {
- e.stopPropagation();
- e.stopImmediatePropagation();
- e.preventDefault();
- // Remove last element and put username to input
- let elements = [];
- let data = this.$select.select2('data');
- for (let el = 0; el < data.length; el++) {
- if (data[el].selected) elements.push(data[el].id);
- }
- this.$parent.find('.select2-search__field').val(elements.pop());
- this.$select.val(elements);
- this.$select.trigger('change');
- this.$element.val(elements.join(","))
- this.lastAcLookup = '';
- setTimeout(() => { this.bind() })
- }
- if (!acResults.isVisible()) {
- return;
- }
- switch (e.keyCode) {
- case 8:
- this.lastAcLookup = '';
- prevent = false; // backspace
- case 40:
- acResults.selectResult(1);
- break; // down
- case 38:
- acResults.selectResult(-1);
- break; // up
- case 27:
- acResults.hideResults();
- break; // esc
- case 13:
- if (!e.shiftKey && acResults.selectedResult !== -1) {
- // Shift isn't pressed
- acResults.insertSelectedResult();
- break;
- }
- // Shift is pressed, allow user to break line
- this.lastAcLookup = '';
- acResults.hideResults(); // enter
- default:
- prevent = false;
- }
- if (prevent) {
- e.stopPropagation();
- e.stopImmediatePropagation();
- e.preventDefault();
- }
- }.bind(this)).on('keyup', function (e) {
- if ([13, 17, 27, 38, 40].indexOf(e.keyCode) !== -1 || e.ctrlKey && e.keyCode === 86) {
- return
- }
- if (this.$parent.find('.select2-search__field').val()) this.triggerAutoComplete(this.$parent.find('.select2-search__field').val().replaceAll("@", "").trim());
- }.bind(this)).on('blur', function () {
- if (this.$parent.find('.select2-search__field').css('width') && !this.$parent.find('.select2-search__field').attr('width')) {
- this.$parent.find('.select2-search__field').attr('width', 'set');
- let padding = parseInt(window.getComputedStyle(this.$parent.find('.select2-selection__rendered').get(0), null).getPropertyValue('padding-left'));
- if (this.$parent.find('.select2-search__field').width() - padding > 0) {
- this.$parent.find('.select2-search__field').css('width', this.$parent.find('.select2-search__field').width() - padding + 'px');
- }
- }
- setTimeout($.context(this, 'hideAutoComplete'), 300)
- }.bind(this)).on('paste', function () {
- setTimeout(() => {
- this.lastAcLookup = '';
- this.triggerAutoComplete(this.$parent.find('.select2-search__field').val().replaceAll("@", "").trim(), true)
- });
- }.bind(this))
- },
- triggerAutoComplete: function (name, instant) {
- if (this.$element.hasClass('AcSingle') && this.$select.select2('data').length) {
- return;
- }
- this.hideAutoComplete();
- this.lastAcLookup = name;
- if (name.length > 2 && name.substr(0, 1) !== '[') {
- this.acLoadTimer = setTimeout($.context(this, 'autoCompleteLookup'), instant ? 0 : 200);
- }
- },
- autoCompleteLookup: function () {
- if (this.acXhr) {
- this.acXhr.abort();
- }
- this.acXhr = XenForo.ajax(
- this.$element.attr('data-acurl') || XenForo.AutoComplete.getDefaultUrl(),
- { q: this.lastAcLookup },
- $.context(this, 'showAutoCompleteResults'),
- { global: false, error: false }
- );
- },
- hideAutoComplete: function () {
- this.acResults.hideResults();
- if (this.acLoadTimer) {
- clearTimeout(this.acLoadTimer);
- this.acLoadTimer = false;
- }
- },
- showAutoCompleteResults: function (ajaxData, def) {
- this.acXhr = false;
- if (this.lastAcLookup !== this.$parent.find('.select2-search__field').val().replaceAll("@", "").trim()) {
- return;
- }
- this.acResults.showResults(
- this.lastAcLookup,
- ajaxData.results,
- this.$parent.find('.select2-search__field')
- );
- },
- }
- XenForo.register('.ChosenAutoComplete', 'XenForo.EnhancedAutoComplete');
- // *********************************************************************
- XenForo.Cachify = function () {
- if (!navigator.serviceWorker) return;
- navigator.serviceWorker.register('/js/lolzteam/sw.js?_v=' + XenForo._jsVersion, { scope: '/' }).then(function (worker) {
- if (worker && worker.active)
- worker.active.postMessage({
- method: 'initCache',
- path: '/cache?_xfResponseType=json&_xfToken=' + XenForo._csrfToken
- })
- }).catch(function (error) {
- console.error('Error deploying service worker:', error);
- });
- };
- XenForo.register('html.Cachify', 'XenForo.Cachify');
- // *********************************************************************
- XenForo.AlertsClear = function ($link) {
- var callback = function (ajaxData, textStatus) {
- if (ajaxData.error) {
- XenForo.hasResponseError(ajaxData, textStatus);
- } else {
- $('.alerts.Popup').data('XenForo.PopupMenu').reload();
- }
- };
- $link.click(function (e) {
- e.preventDefault();
- const alertType = $('.alertsTabs').find('.active').data('id');
- XenForo.ajax($link.attr('href'), { type: alertType }, callback);
- });
- };
- XenForo.register('a.AlertsClear', 'XenForo.AlertsClear');
- // *********************************************************************
- XenForo.ClickableTabs = function ($element) {
- $element.find('li').on('click', function (e) {
- if (e.target.tagName !== "LI" || !$(this).find('a').length) return;
- e.preventDefault();
- e.stopPropagation();
- e.stopImmediatePropagation();
- $(this).find('a')[0].click();
- })
- };
- XenForo.register('.tabs', 'XenForo.ClickableTabs');
- XenForo.AlertReader = function ($element) {
- $element.parent().on('click mouseover', function () {
- localStorage.setItem('read_notifies', + new Date());
- });
- $(window).on('storage', function (e) {
- if (e.originalEvent.key === 'read_notifies') {
- localStorage.removeItem('read_notifies');
- const badge = $("#AlertsMenu_Counter");
- if (!badge.hasClass('Zero')) {
- badge.addClass('Zero');
- }
- }
- })
- };
- /**
- * не юзайте больше обычный tippy, заебали пастить настройки
- * обдрочитесь же заменять когда обновлять будете
- * ваще по хорошему надо все отрефакторить на эту функу
- */
- XenForo.tippy = function (target, options = {}, preset = 'tooltip') {
- if (typeof preset === 'string') preset = preset.split(' ')
- const presets = {
- default: {
- zIndex: $(target).closest('.xenOverlay').length ? 11111 : 9000, // overlay fix
- boundary: 'viewport',
- distance: 5,
- arrow: true,
- animation: 'shift-toward',
- trigger: XenForo.isTouchBrowser() ? 'click' : 'mouseenter focus',
- appendTo: $(target).closest('.modal, body')[0] // https://zelenka.guru/threads/4730409/
- },
- tooltip: {
- hideOnClick: true,
- },
- popup: {
- theme: 'popup',
- placement: 'top',
- interactive: true,
- hideOnClick: XenForo.isTouchBrowser()
- }
- }
- return tippy(target, $.extend(
- presets.default,
- ...preset.map(p => presets[p]),
- options
- ))
- }
- if (supports_html5_storage()) {
- XenForo.register('a[href="account/alerts"]', 'XenForo.AlertReader');
- }
- XenForo.ContactDetailsForm = function ($element) {
- const secretAnswer = $element.find("#secretAnswer");
- $element.find('input[type="email"]').on('input', () => {
- if (!secretAnswer.is(':visible')) {
- secretAnswer.show();
- }
- });
- }
- XenForo.register('form.ContactDetailsForm', 'XenForo.ContactDetailsForm');
- }(jQuery);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement