/*!
* SmartMenus jQuery Plugin - v1.0.1 - November 1, 2016
* http://www.smartmenus.org/
*
* Copyright Vasil Dinkov, Vadikom Web Ltd.
* http://vadikom.com
*
* Licensed MIT
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof module === 'object' && typeof module.exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else {
// Global jQuery
factory(jQuery);
}
}(function ($) {
var menuTrees = [],
IE = !!window.createPopup, // detect it for the iframe shim
mouse = false, // optimize for touch by default - we will detect for mouse input
touchEvents = 'ontouchstart' in window, // we use this just to choose between toucn and pointer events, not for touch screen detection
mouseDetectionEnabled = false,
requestAnimationFrame = window.requestAnimationFrame || function (callback) { return setTimeout(callback, 1000 / 60); },
cancelAnimationFrame = window.cancelAnimationFrame || function (id) { clearTimeout(id); };
// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
function initMouseDetection(disable) {
var eNS = '.smartmenus_mouse';
if (!mouseDetectionEnabled && !disable) {
// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
// in practice, this seems like impossible to trick unintentianally with a real mouse and a pretty safe detection on touch devices (even with older browsers that do not support touch events)
var firstTime = true,
lastMove = null;
$(document).bind(getEventsNS([
['mousemove', function (e) {
var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
if (lastMove) {
var deltaX = Math.abs(lastMove.x - thisMove.x),
deltaY = Math.abs(lastMove.y - thisMove.y);
if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
mouse = true;
// if this is the first check after page load, check if we are not over some item by chance and call the mouseenter handler if yes
if (firstTime) {
var $a = $(e.target).closest('a');
if ($a.is('a')) {
$.each(menuTrees, function () {
if ($.contains(this.$root[0], $a[0])) {
this.itemEnter({ currentTarget: $a[0] });
return false;
}
});
}
firstTime = false;
}
}
}
lastMove = thisMove;
}],
[touchEvents ? 'touchstart' : 'pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut', function (e) {
if (isTouchEvent(e.originalEvent)) {
mouse = false;
}
}]
], eNS));
mouseDetectionEnabled = true;
} else if (mouseDetectionEnabled && disable) {
$(document).unbind(eNS);
mouseDetectionEnabled = false;
}
}
function isTouchEvent(e) {
return !/^(4|mouse)$/.test(e.pointerType);
}
// returns a jQuery bind() ready object
function getEventsNS(defArr, eNS) {
if (!eNS) {
eNS = '';
}
var obj = {};
$.each(defArr, function (index, value) {
obj[value[0].split(' ').join(eNS + ' ') + eNS] = value[1];
});
return obj;
}
$.SmartMenus = function (elm, options) {
this.$root = $(elm);
this.opts = options;
this.rootId = ''; // internal
this.accessIdPrefix = '';
this.$subArrow = null;
this.activatedItems = []; // stores last activated A's for each level
this.visibleSubMenus = []; // stores visible sub menus UL's (might be in no particular order)
this.showTimeout = 0;
this.hideTimeout = 0;
this.scrollTimeout = 0;
this.clickActivated = false;
this.focusActivated = false;
this.zIndexInc = 0;
this.idInc = 0;
this.$firstLink = null; // we'll use these for some tests
this.$firstSub = null; // at runtime so we'll cache them
this.disabled = false;
this.$disableOverlay = null;
this.$touchScrollingSub = null;
this.cssTransforms3d = 'perspective' in elm.style || 'webkitPerspective' in elm.style;
this.wasCollapsible = false;
this.init();
};
$.extend($.SmartMenus, {
hideAll: function () {
$.each(menuTrees, function () {
this.menuHideAll();
});
},
destroy: function () {
while (menuTrees.length) {
menuTrees[0].destroy();
}
initMouseDetection(true);
},
prototype: {
init: function (refresh) {
var self = this;
if (!refresh) {
menuTrees.push(this);
this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
this.accessIdPrefix = 'sm-' + this.rootId + '-';
if (this.$root.hasClass('sm-rtl')) {
this.opts.rightToLeftSubMenus = true;
}
// init root (main menu)
var eNS = '.smartmenus';
this.$root
.data('smartmenus', this)
.attr('data-smartmenus-id', this.rootId)
.dataSM('level', 1)
.bind(getEventsNS([
['mouseover focusin', $.proxy(this.rootOver, this)],
['mouseout focusout', $.proxy(this.rootOut, this)],
['keydown', $.proxy(this.rootKeyDown, this)]
], eNS))
.delegate('a', getEventsNS([
['mouseenter', $.proxy(this.itemEnter, this)],
['mouseleave', $.proxy(this.itemLeave, this)],
['mousedown', $.proxy(this.itemDown, this)],
['focus', $.proxy(this.itemFocus, this)],
['blur', $.proxy(this.itemBlur, this)],
['click', $.proxy(this.itemClick, this)]
], eNS));
// hide menus on tap or click outside the root UL
eNS += this.rootId;
if (this.opts.hideOnClick) {
$(document).bind(getEventsNS([
['touchstart', $.proxy(this.docTouchStart, this)],
['touchmove', $.proxy(this.docTouchMove, this)],
['touchend', $.proxy(this.docTouchEnd, this)],
// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
['click', $.proxy(this.docClick, this)]
], eNS));
}
// hide sub menus on resize
$(window).bind(getEventsNS([['resize orientationchange', $.proxy(this.winResize, this)]], eNS));
if (this.opts.subIndicators) {
this.$subArrow = $('').addClass('sub-arrow');
if (this.opts.subIndicatorsText) {
this.$subArrow.html(this.opts.subIndicatorsText);
}
}
// make sure mouse detection is enabled
initMouseDetection();
}
// init sub menus
this.$firstSub = this.$root.find('ul').each(function () { self.menuInit($(this)); }).eq(0);
this.$firstLink = this.$root.find('a').eq(0);
// find current item
if (this.opts.markCurrentItem) {
var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
reHash = /#.*/,
locHref = window.location.href.replace(reDefaultDoc, ''),
locHrefNoHash = locHref.replace(reHash, '');
this.$root.find('a').each(function () {
var href = this.href.replace(reDefaultDoc, ''),
$this = $(this);
if (href == locHref || href == locHrefNoHash) {
$this.addClass('current');
if (self.opts.markCurrentTree) {
$this.parentsUntil('[data-smartmenus-id]', 'ul').each(function () {
$(this).dataSM('parent-a').addClass('current');
});
}
}
});
}
// save initial state
this.wasCollapsible = this.isCollapsible();
},
destroy: function (refresh) {
if (!refresh) {
var eNS = '.smartmenus';
this.$root
.removeData('smartmenus')
.removeAttr('data-smartmenus-id')
.removeDataSM('level')
.unbind(eNS)
.undelegate(eNS);
eNS += this.rootId;
$(document).unbind(eNS);
$(window).unbind(eNS);
if (this.opts.subIndicators) {
this.$subArrow = null;
}
}
this.menuHideAll();
var self = this;
this.$root.find('ul').each(function () {
var $this = $(this);
if ($this.dataSM('scroll-arrows')) {
$this.dataSM('scroll-arrows').remove();
}
if ($this.dataSM('shown-before')) {
if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
$this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
}
if ($this.dataSM('scroll-arrows')) {
$this.dataSM('scroll-arrows').remove();
}
$this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
}
if (($this.attr('id') || '').indexOf(self.accessIdPrefix) == 0) {
$this.removeAttr('id');
}
})
.removeDataSM('in-mega')
.removeDataSM('shown-before')
.removeDataSM('ie-shim')
.removeDataSM('scroll-arrows')
.removeDataSM('parent-a')
.removeDataSM('level')
.removeDataSM('beforefirstshowfired')
.removeAttr('role')
.removeAttr('aria-hidden')
.removeAttr('aria-labelledby')
.removeAttr('aria-expanded');
this.$root.find('a.has-submenu').each(function () {
var $this = $(this);
if ($this.attr('id').indexOf(self.accessIdPrefix) == 0) {
$this.removeAttr('id');
}
})
.removeClass('has-submenu')
.removeDataSM('sub')
.removeAttr('aria-haspopup')
.removeAttr('aria-controls')
.removeAttr('aria-expanded')
.closest('li').removeDataSM('sub');
if (this.opts.subIndicators) {
this.$root.find('span.sub-arrow').remove();
}
if (this.opts.markCurrentItem) {
this.$root.find('a.current').removeClass('current');
}
if (!refresh) {
this.$root = null;
this.$firstLink = null;
this.$firstSub = null;
if (this.$disableOverlay) {
this.$disableOverlay.remove();
this.$disableOverlay = null;
}
menuTrees.splice($.inArray(this, menuTrees), 1);
}
},
disable: function (noOverlay) {
if (!this.disabled) {
this.menuHideAll();
// display overlay over the menu to prevent interaction
if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
var pos = this.$root.offset();
this.$disableOverlay = $('
').css({
position: 'absolute',
top: pos.top,
left: pos.left,
width: this.$root.outerWidth(),
height: this.$root.outerHeight(),
zIndex: this.getStartZIndex(true),
opacity: 0
}).appendTo(document.body);
}
this.disabled = true;
}
},
docClick: function (e) {
if (this.$touchScrollingSub) {
this.$touchScrollingSub = null;
return;
}
// hide on any click outside the menu or on a menu link
if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
this.menuHideAll();
}
},
docTouchEnd: function (e) {
if (!this.lastTouch) {
return;
}
if (this.visibleSubMenus.length && (this.lastTouch.x2 === undefined || this.lastTouch.x1 == this.lastTouch.x2) && (this.lastTouch.y2 === undefined || this.lastTouch.y1 == this.lastTouch.y2) && (!this.lastTouch.target || !$.contains(this.$root[0], this.lastTouch.target))) {
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
this.hideTimeout = 0;
}
// hide with a delay to prevent triggering accidental unwanted click on some page element
var self = this;
this.hideTimeout = setTimeout(function () { self.menuHideAll(); }, 350);
}
this.lastTouch = null;
},
docTouchMove: function (e) {
if (!this.lastTouch) {
return;
}
var touchPoint = e.originalEvent.touches[0];
this.lastTouch.x2 = touchPoint.pageX;
this.lastTouch.y2 = touchPoint.pageY;
},
docTouchStart: function (e) {
var touchPoint = e.originalEvent.touches[0];
this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
},
enable: function () {
if (this.disabled) {
if (this.$disableOverlay) {
this.$disableOverlay.remove();
this.$disableOverlay = null;
}
this.disabled = false;
}
},
getClosestMenu: function (elm) {
var $closestMenu = $(elm).closest('ul');
while ($closestMenu.dataSM('in-mega')) {
$closestMenu = $closestMenu.parent().closest('ul');
}
return $closestMenu[0] || null;
},
getHeight: function ($elm) {
return this.getOffset($elm, true);
},
// returns precise width/height float values
getOffset: function ($elm, height) {
var old;
if ($elm.css('display') == 'none') {
old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
$elm.css({ position: 'absolute', visibility: 'hidden' }).show();
}
var box = $elm[0].getBoundingClientRect && $elm[0].getBoundingClientRect(),
val = box && (height ? box.height || box.bottom - box.top : box.width || box.right - box.left);
if (!val && val !== 0) {
val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
}
if (old) {
$elm.hide().css(old);
}
return val;
},
getStartZIndex: function (root) {
var zIndex = parseInt(this[root ? '$root' : '$firstSub'].css('z-index'));
if (!root && isNaN(zIndex)) {
zIndex = parseInt(this.$root.css('z-index'));
}
return !isNaN(zIndex) ? zIndex : 1;
},
getTouchPoint: function (e) {
return e.touches && e.touches[0] || e.changedTouches && e.changedTouches[0] || e;
},
getViewport: function (height) {
var name = height ? 'Height' : 'Width',
val = document.documentElement['client' + name],
val2 = window['inner' + name];
if (val2) {
val = Math.min(val, val2);
}
return val;
},
getViewportHeight: function () {
return this.getViewport(true);
},
getViewportWidth: function () {
return this.getViewport();
},
getWidth: function ($elm) {
return this.getOffset($elm);
},
handleEvents: function () {
return !this.disabled && this.isCSSOn();
},
handleItemEvents: function ($a) {
return this.handleEvents() && !this.isLinkInMegaMenu($a);
},
isCollapsible: function () {
return this.$firstSub.css('position') == 'static';
},
isCSSOn: function () {
return this.$firstLink.css('display') == 'block';
},
isFixed: function () {
var isFixed = this.$root.css('position') == 'fixed';
if (!isFixed) {
this.$root.parentsUntil('body').each(function () {
if ($(this).css('position') == 'fixed') {
isFixed = true;
return false;
}
});
}
return isFixed;
},
isLinkInMegaMenu: function ($a) {
return $(this.getClosestMenu($a[0])).hasClass('mega-menu');
},
isTouchMode: function () {
return !mouse || this.opts.noMouseOver || this.isCollapsible();
},
itemActivate: function ($a, focus) {
var $ul = $a.closest('ul'),
level = $ul.dataSM('level');
// if for some reason the parent item is not activated (e.g. this is an API call to activate the item), activate all parent items first
if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
var self = this;
$($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function () {
self.itemActivate($(this).dataSM('parent-a'));
});
}
// hide any visible deeper level sub menus
if (!this.isCollapsible() || focus) {
this.menuHideSubMenus(!this.activatedItems[level - 1] || this.activatedItems[level - 1][0] != $a[0] ? level - 1 : level);
}
// save new active item for this level
this.activatedItems[level - 1] = $a;
if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
return;
}
// show the sub menu if this item has one
var $sub = $a.dataSM('sub');
if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
this.menuShow($sub);
}
},
itemBlur: function (e) {
var $a = $(e.currentTarget);
if (!this.handleItemEvents($a)) {
return;
}
this.$root.triggerHandler('blur.smapi', $a[0]);
},
itemClick: function (e) {
var $a = $(e.currentTarget);
if (!this.handleItemEvents($a)) {
return;
}
if (this.$touchScrollingSub && this.$touchScrollingSub[0] == $a.closest('ul')[0]) {
this.$touchScrollingSub = null;
e.stopPropagation();
return false;
}
if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
return false;
}
var subArrowClicked = $(e.target).is('span.sub-arrow'),
$sub = $a.dataSM('sub'),
firstLevelSub = $sub ? $sub.dataSM('level') == 2 : false;
// if the sub is not visible
if ($sub && !$sub.is(':visible')) {
if (this.opts.showOnClick && firstLevelSub) {
this.clickActivated = true;
}
// try to activate the item and show the sub
this.itemActivate($a);
// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
// if it couldn't show it, then the sub menus are disabled with an !important declaration (e.g. via mobile styles) so let the link get loaded
if ($sub.is(':visible')) {
this.focusActivated = true;
return false;
}
} else if (this.isCollapsible() && subArrowClicked) {
this.itemActivate($a);
this.menuHide($sub);
return false;
}
if (this.opts.showOnClick && firstLevelSub || $a.hasClass('disabled') || this.$root.triggerHandler('select.smapi', $a[0]) === false) {
return false;
}
},
itemDown: function (e) {
var $a = $(e.currentTarget);
if (!this.handleItemEvents($a)) {
return;
}
$a.dataSM('mousedown', true);
},
itemEnter: function (e) {
var $a = $(e.currentTarget);
if (!this.handleItemEvents($a)) {
return;
}
if (!this.isTouchMode()) {
if (this.showTimeout) {
clearTimeout(this.showTimeout);
this.showTimeout = 0;
}
var self = this;
this.showTimeout = setTimeout(function () { self.itemActivate($a); }, this.opts.showOnClick && $a.closest('ul').dataSM('level') == 1 ? 1 : this.opts.showTimeout);
}
this.$root.triggerHandler('mouseenter.smapi', $a[0]);
},
itemFocus: function (e) {
var $a = $(e.currentTarget);
if (!this.handleItemEvents($a)) {
return;
}
// fix (the mousedown check): in some browsers a tap/click produces consecutive focus + click events so we don't need to activate the item on focus
if (this.focusActivated && (!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
this.itemActivate($a, true);
}
this.$root.triggerHandler('focus.smapi', $a[0]);
},
itemLeave: function (e) {
var $a = $(e.currentTarget);
if (!this.handleItemEvents($a)) {
return;
}
if (!this.isTouchMode()) {
$a[0].blur();
if (this.showTimeout) {
clearTimeout(this.showTimeout);
this.showTimeout = 0;
}
}
$a.removeDataSM('mousedown');
this.$root.triggerHandler('mouseleave.smapi', $a[0]);
},
menuHide: function ($sub) {
if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
return;
}
$sub.stop(true, true);
if ($sub.css('display') != 'none') {
var complete = function () {
// unset z-index
$sub.css('z-index', '');
};
// if sub is collapsible (mobile view)
if (this.isCollapsible()) {
if (this.opts.collapsibleHideFunction) {
this.opts.collapsibleHideFunction.call(this, $sub, complete);
} else {
$sub.hide(this.opts.collapsibleHideDuration, complete);
}
} else {
if (this.opts.hideFunction) {
this.opts.hideFunction.call(this, $sub, complete);
} else {
$sub.hide(this.opts.hideDuration, complete);
}
}
// remove IE iframe shim
if ($sub.dataSM('ie-shim')) {
$sub.dataSM('ie-shim').remove().css({ '-webkit-transform': '', transform: '' });
}
// deactivate scrolling if it is activated for this sub
if ($sub.dataSM('scroll')) {
this.menuScrollStop($sub);
$sub.css({ 'touch-action': '', '-ms-touch-action': '', '-webkit-transform': '', transform: '' })
.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
}
// unhighlight parent item + accessibility
$sub.dataSM('parent-a').removeClass('highlighted').attr('aria-expanded', 'false');
$sub.attr({
'aria-expanded': 'false',
'aria-hidden': 'true'
});
var level = $sub.dataSM('level');
this.activatedItems.splice(level - 1, 1);
this.visibleSubMenus.splice($.inArray($sub, this.visibleSubMenus), 1);
this.$root.triggerHandler('hide.smapi', $sub[0]);
}
},
menuHideAll: function () {
if (this.showTimeout) {
clearTimeout(this.showTimeout);
this.showTimeout = 0;
}
// hide all subs
// if it's a popup, this.visibleSubMenus[0] is the root UL
var level = this.opts.isPopup ? 1 : 0;
for (var i = this.visibleSubMenus.length - 1; i >= level; i--) {
this.menuHide(this.visibleSubMenus[i]);
}
// hide root if it's popup
if (this.opts.isPopup) {
this.$root.stop(true, true);
if (this.$root.is(':visible')) {
if (this.opts.hideFunction) {
this.opts.hideFunction.call(this, this.$root);
} else {
this.$root.hide(this.opts.hideDuration);
}
// remove IE iframe shim
if (this.$root.dataSM('ie-shim')) {
this.$root.dataSM('ie-shim').remove();
}
}
}
this.activatedItems = [];
this.visibleSubMenus = [];
this.clickActivated = false;
this.focusActivated = false;
// reset z-index increment
this.zIndexInc = 0;
this.$root.triggerHandler('hideAll.smapi');
},
menuHideSubMenus: function (level) {
for (var i = this.activatedItems.length - 1; i >= level; i--) {
var $sub = this.activatedItems[i].dataSM('sub');
if ($sub) {
this.menuHide($sub);
}
}
},
menuIframeShim: function ($ul) {
// create iframe shim for the menu
if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
$ul.dataSM('ie-shim', $('').attr({ src: 'javascript:0', tabindex: -9 })
.css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
);
}
},
menuInit: function ($ul) {
if (!$ul.dataSM('in-mega')) {
// mark UL's in mega drop downs (if any) so we can neglect them
if ($ul.hasClass('mega-menu')) {
$ul.find('ul').dataSM('in-mega', true);
}
// get level (much faster than, for example, using parentsUntil)
var level = 2,
par = $ul[0];
while ((par = par.parentNode.parentNode) != this.$root[0]) {
level++;
}
// cache stuff for quick access
var $a = $ul.prevAll('a').eq(-1);
// if the link is nested (e.g. in a heading)
if (!$a.length) {
$a = $ul.prevAll().find('a').eq(-1);
}
$a.addClass('has-submenu').dataSM('sub', $ul);
$ul.dataSM('parent-a', $a)
.dataSM('level', level)
.parent().dataSM('sub', $ul);
// accessibility
var aId = $a.attr('id') || this.accessIdPrefix + (++this.idInc),
ulId = $ul.attr('id') || this.accessIdPrefix + (++this.idInc);
$a.attr({
id: aId,
'aria-haspopup': 'true',
'aria-controls': ulId,
'aria-expanded': 'false'
});
$ul.attr({
id: ulId,
'role': 'group',
'aria-hidden': 'true',
'aria-labelledby': aId,
'aria-expanded': 'false'
});
// add sub indicator to parent item
if (this.opts.subIndicators) {
$a[this.opts.subIndicatorsPos](this.$subArrow.clone());
}
}
},
menuPosition: function ($sub) {
var $a = $sub.dataSM('parent-a'),
$li = $a.closest('li'),
$ul = $li.parent(),
level = $sub.dataSM('level'),
subW = this.getWidth($sub),
subH = this.getHeight($sub),
itemOffset = $a.offset(),
itemX = itemOffset.left,
itemY = itemOffset.top,
itemW = this.getWidth($a),
itemH = this.getHeight($a),
$win = $(window),
winX = $win.scrollLeft(),
winY = $win.scrollTop(),
winW = this.getViewportWidth(),
winH = this.getViewportHeight(),
horizontalParent = $ul.parent().is('[data-sm-horizontal-sub]') || level == 2 && !$ul.hasClass('sm-vertical'),
rightToLeft = this.opts.rightToLeftSubMenus && !$li.is('[data-sm-reverse]') || !this.opts.rightToLeftSubMenus && $li.is('[data-sm-reverse]'),
subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
x, y;
if (horizontalParent) {
x = rightToLeft ? itemW - subW - subOffsetX : subOffsetX;
y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
} else {
x = rightToLeft ? subOffsetX - subW : itemW - subOffsetX;
y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
}
if (this.opts.keepInViewport) {
var absX = itemX + x,
absY = itemY + y;
if (rightToLeft && absX < winX) {
x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
} else if (!rightToLeft && absX + subW > winX + winW) {
x = horizontalParent ? winX + winW - subW - absX + x : subOffsetX - subW;
}
if (!horizontalParent) {
if (subH < winH && absY + subH > winY + winH) {
y += winY + winH - subH - absY;
} else if (subH >= winH || absY < winY) {
y += winY - absY;
}
}
// do we need scrolling?
// 0.49 used for better precision when dealing with float values
if (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49) {
var self = this;
if (!$sub.dataSM('scroll-arrows')) {
$sub.dataSM('scroll-arrows', $([$('')[0], $('')[0]])
.bind({
mouseenter: function () {
$sub.dataSM('scroll').up = $(this).hasClass('scroll-up');
self.menuScroll($sub);
},
mouseleave: function (e) {
self.menuScrollStop($sub);
self.menuScrollOut($sub, e);
},
'mousewheel DOMMouseScroll': function (e) { e.preventDefault(); }
})
.insertAfter($sub)
);
}
// bind scroll events and save scroll data for this sub
var eNS = '.smartmenus_scroll';
$sub.dataSM('scroll', {
y: this.cssTransforms3d ? 0 : y - itemH,
step: 1,
// cache stuff for faster recalcs later
itemH: itemH,
subH: subH,
arrowDownH: this.getHeight($sub.dataSM('scroll-arrows').eq(1))
})
.bind(getEventsNS([
['mouseover', function (e) { self.menuScrollOver($sub, e); }],
['mouseout', function (e) { self.menuScrollOut($sub, e); }],
['mousewheel DOMMouseScroll', function (e) { self.menuScrollMousewheel($sub, e); }]
], eNS))
.dataSM('scroll-arrows').css({ top: 'auto', left: '0', marginLeft: x + (parseInt($sub.css('border-left-width')) || 0), width: subW - (parseInt($sub.css('border-left-width')) || 0) - (parseInt($sub.css('border-right-width')) || 0), zIndex: $sub.css('z-index') })
.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
// when a menu tree is fixed positioned we allow scrolling via touch too
// since there is no other way to access such long sub menus if no mouse is present
if (this.isFixed()) {
$sub.css({ 'touch-action': 'none', '-ms-touch-action': 'none' })
.bind(getEventsNS([
[touchEvents ? 'touchstart touchmove touchend' : 'pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp', function (e) {
self.menuScrollTouch($sub, e);
}]
], eNS));
}
}
}
$sub.css({ top: 'auto', left: '0', marginLeft: x, marginTop: y - itemH });
// IE iframe shim
this.menuIframeShim($sub);
if ($sub.dataSM('ie-shim')) {
$sub.dataSM('ie-shim').css({ zIndex: $sub.css('z-index'), width: subW, height: subH, marginLeft: x, marginTop: y - itemH });
}
},
menuScroll: function ($sub, once, step) {
var data = $sub.dataSM('scroll'),
$arrows = $sub.dataSM('scroll-arrows'),
end = data.up ? data.upEnd : data.downEnd,
diff;
if (!once && data.momentum) {
data.momentum *= 0.92;
diff = data.momentum;
if (diff < 0.5) {
this.menuScrollStop($sub);
return;
}
} else {
diff = step || (once || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor(data.step));
}
// hide any visible deeper level sub menus
var level = $sub.dataSM('level');
if (this.activatedItems[level - 1] && this.activatedItems[level - 1].dataSM('sub') && this.activatedItems[level - 1].dataSM('sub').is(':visible')) {
this.menuHideSubMenus(level - 1);
}
data.y = data.up && end <= data.y || !data.up && end >= data.y ? data.y : (Math.abs(end - data.y) > diff ? data.y + (data.up ? diff : -diff) : end);
$sub.add($sub.dataSM('ie-shim')).css(this.cssTransforms3d ? { '-webkit-transform': 'translate3d(0, ' + data.y + 'px, 0)', transform: 'translate3d(0, ' + data.y + 'px, 0)' } : { marginTop: data.y });
// show opposite arrow if appropriate
if (mouse && (data.up && data.y > data.downEnd || !data.up && data.y < data.upEnd)) {
$arrows.eq(data.up ? 1 : 0).show();
}
// if we've reached the end
if (data.y == end) {
if (mouse) {
$arrows.eq(data.up ? 0 : 1).hide();
}
this.menuScrollStop($sub);
} else if (!once) {
if (this.opts.scrollAccelerate && data.step < this.opts.scrollStep) {
data.step += 0.2;
}
var self = this;
this.scrollTimeout = requestAnimationFrame(function () { self.menuScroll($sub); });
}
},
menuScrollMousewheel: function ($sub, e) {
if (this.getClosestMenu(e.target) == $sub[0]) {
e = e.originalEvent;
var up = (e.wheelDelta || -e.detail) > 0;
if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
$sub.dataSM('scroll').up = up;
this.menuScroll($sub, true);
}
}
e.preventDefault();
},
menuScrollOut: function ($sub, e) {
if (mouse) {
if (!/^scroll-(up|down)/.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || this.getClosestMenu(e.relatedTarget) != $sub[0])) {
$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
}
}
},
menuScrollOver: function ($sub, e) {
if (mouse) {
if (!/^scroll-(up|down)/.test(e.target.className) && this.getClosestMenu(e.target) == $sub[0]) {
this.menuScrollRefreshData($sub);
var data = $sub.dataSM('scroll'),
upEnd = $(window).scrollTop() - $sub.dataSM('parent-a').offset().top - data.itemH;
$sub.dataSM('scroll-arrows').eq(0).css('margin-top', upEnd).end()
.eq(1).css('margin-top', upEnd + this.getViewportHeight() - data.arrowDownH).end()
.css('visibility', 'visible');
}
}
},
menuScrollRefreshData: function ($sub) {
var data = $sub.dataSM('scroll'),
upEnd = $(window).scrollTop() - $sub.dataSM('parent-a').offset().top - data.itemH;
if (this.cssTransforms3d) {
upEnd = -(parseFloat($sub.css('margin-top')) - upEnd);
}
$.extend(data, {
upEnd: upEnd,
downEnd: upEnd + this.getViewportHeight() - data.subH
});
},
menuScrollStop: function ($sub) {
if (this.scrollTimeout) {
cancelAnimationFrame(this.scrollTimeout);
this.scrollTimeout = 0;
$sub.dataSM('scroll').step = 1;
return true;
}
},
menuScrollTouch: function ($sub, e) {
e = e.originalEvent;
if (isTouchEvent(e)) {
var touchPoint = this.getTouchPoint(e);
// neglect event if we touched a visible deeper level sub menu
if (this.getClosestMenu(touchPoint.target) == $sub[0]) {
var data = $sub.dataSM('scroll');
if (/(start|down)$/i.test(e.type)) {
if (this.menuScrollStop($sub)) {
// if we were scrolling, just stop and don't activate any link on the first touch
e.preventDefault();
this.$touchScrollingSub = $sub;
} else {
this.$touchScrollingSub = null;
}
// update scroll data since the user might have zoomed, etc.
this.menuScrollRefreshData($sub);
// extend it with the touch properties
$.extend(data, {
touchStartY: touchPoint.pageY,
touchStartTime: e.timeStamp
});
} else if (/move$/i.test(e.type)) {
var prevY = data.touchY !== undefined ? data.touchY : data.touchStartY;
if (prevY !== undefined && prevY != touchPoint.pageY) {
this.$touchScrollingSub = $sub;
var up = prevY < touchPoint.pageY;
// changed direction? reset...
if (data.up !== undefined && data.up != up) {
$.extend(data, {
touchStartY: touchPoint.pageY,
touchStartTime: e.timeStamp
});
}
$.extend(data, {
up: up,
touchY: touchPoint.pageY
});
this.menuScroll($sub, true, Math.abs(touchPoint.pageY - prevY));
}
e.preventDefault();
} else { // touchend/pointerup
if (data.touchY !== undefined) {
if (data.momentum = Math.pow(Math.abs(touchPoint.pageY - data.touchStartY) / (e.timeStamp - data.touchStartTime), 2) * 15) {
this.menuScrollStop($sub);
this.menuScroll($sub);
e.preventDefault();
}
delete data.touchY;
}
}
}
}
},
menuShow: function ($sub) {
if (!$sub.dataSM('beforefirstshowfired')) {
$sub.dataSM('beforefirstshowfired', true);
if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
return;
}
}
if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
return;
}
$sub.dataSM('shown-before', true)
.stop(true, true);
if (!$sub.is(':visible')) {
// highlight parent item
var $a = $sub.dataSM('parent-a');
if (this.opts.keepHighlighted || this.isCollapsible()) {
$a.addClass('highlighted');
}
if (this.isCollapsible()) {
$sub.removeClass('sm-nowrap').css({ zIndex: '', width: 'auto', minWidth: '', maxWidth: '', top: '', left: '', marginLeft: '', marginTop: '' });
} else {
// set z-index
$sub.css('z-index', this.zIndexInc = (this.zIndexInc || this.getStartZIndex()) + 1);
// min/max-width fix - no way to rely purely on CSS as all UL's are nested
if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
$sub.css({ width: 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
if (this.opts.subMenusMinWidth) {
$sub.css('min-width', this.opts.subMenusMinWidth);
}
if (this.opts.subMenusMaxWidth) {
var noMaxWidth = this.getWidth($sub);
$sub.css('max-width', this.opts.subMenusMaxWidth);
if (noMaxWidth > this.getWidth($sub)) {
$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
}
}
}
this.menuPosition($sub);
// insert IE iframe shim
if ($sub.dataSM('ie-shim')) {
$sub.dataSM('ie-shim').insertBefore($sub);
}
}
var complete = function () {
// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
$sub.css('overflow', '');
};
// if sub is collapsible (mobile view)
if (this.isCollapsible()) {
if (this.opts.collapsibleShowFunction) {
this.opts.collapsibleShowFunction.call(this, $sub, complete);
} else {
$sub.show(this.opts.collapsibleShowDuration, complete);
}
} else {
if (this.opts.showFunction) {
this.opts.showFunction.call(this, $sub, complete);
} else {
$sub.show(this.opts.showDuration, complete);
}
}
// accessibility
$a.attr('aria-expanded', 'true');
$sub.attr({
'aria-expanded': 'true',
'aria-hidden': 'false'
});
// store sub menu in visible array
this.visibleSubMenus.push($sub);
this.$root.triggerHandler('show.smapi', $sub[0]);
}
},
popupHide: function (noHideTimeout) {
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
this.hideTimeout = 0;
}
var self = this;
this.hideTimeout = setTimeout(function () {
self.menuHideAll();
}, noHideTimeout ? 1 : this.opts.hideTimeout);
},
popupShow: function (left, top) {
if (!this.opts.isPopup) {
alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
return;
}
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
this.hideTimeout = 0;
}
this.$root.dataSM('shown-before', true)
.stop(true, true);
if (!this.$root.is(':visible')) {
this.$root.css({ left: left, top: top });
// IE iframe shim
this.menuIframeShim(this.$root);
if (this.$root.dataSM('ie-shim')) {
this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left: left, top: top }).insertBefore(this.$root);
}
// show menu
var self = this,
complete = function () {
self.$root.css('overflow', '');
};
if (this.opts.showFunction) {
this.opts.showFunction.call(this, this.$root, complete);
} else {
this.$root.show(this.opts.showDuration, complete);
}
this.visibleSubMenus[0] = this.$root;
}
},
refresh: function () {
this.destroy(true);
this.init(true);
},
rootKeyDown: function (e) {
if (!this.handleEvents()) {
return;
}
switch (e.keyCode) {
case 27: // reset on Esc
var $activeTopItem = this.activatedItems[0];
if ($activeTopItem) {
this.menuHideAll();
$activeTopItem[0].focus();
var $sub = $activeTopItem.dataSM('sub');
if ($sub) {
this.menuHide($sub);
}
}
break;
case 32: // activate item's sub on Space
var $target = $(e.target);
if ($target.is('a') && this.handleItemEvents($target)) {
var $sub = $target.dataSM('sub');
if ($sub && !$sub.is(':visible')) {
this.itemClick({ currentTarget: e.target });
e.preventDefault();
}
}
break;
}
},
rootOut: function (e) {
if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
return;
}
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
this.hideTimeout = 0;
}
if (!this.opts.showOnClick || !this.opts.hideOnClick) {
var self = this;
this.hideTimeout = setTimeout(function () { self.menuHideAll(); }, this.opts.hideTimeout);
}
},
rootOver: function (e) {
if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
return;
}
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
this.hideTimeout = 0;
}
},
winResize: function (e) {
if (!this.handleEvents()) {
// we still need to resize the disable overlay if it's visible
if (this.$disableOverlay) {
var pos = this.$root.offset();
this.$disableOverlay.css({
top: pos.top,
left: pos.left,
width: this.$root.outerWidth(),
height: this.$root.outerHeight()
});
}
return;
}
// hide sub menus on resize - on mobile do it only on orientation change
if (!('onorientationchange' in window) || e.type == 'orientationchange') {
var isCollapsible = this.isCollapsible();
// if it was collapsible before resize and still is, don't do it
if (!(this.wasCollapsible && isCollapsible)) {
if (this.activatedItems.length) {
this.activatedItems[this.activatedItems.length - 1][0].blur();
}
this.menuHideAll();
}
this.wasCollapsible = isCollapsible;
}
}
}
});
$.fn.dataSM = function (key, val) {
if (val) {
return this.data(key + '_smartmenus', val);
}
return this.data(key + '_smartmenus');
};
$.fn.removeDataSM = function (key) {
return this.removeData(key + '_smartmenus');
};
$.fn.smartmenus = function (options) {
if (typeof options == 'string') {
var args = arguments,
method = options;
Array.prototype.shift.call(args);
return this.each(function () {
var smartmenus = $(this).data('smartmenus');
if (smartmenus && smartmenus[method]) {
smartmenus[method].apply(smartmenus, args);
}
});
}
// [data-sm-options] attribute on the root UL
var dataOpts = this.data('sm-options') || null;
if (dataOpts) {
try {
dataOpts = eval('(' + dataOpts + ')');
} catch (e) {
dataOpts = null;
alert('ERROR\n\nSmartMenus jQuery init:\nInvalid "data-sm-options" attribute value syntax.');
};
}
return this.each(function () {
new $.SmartMenus(this, $.extend({}, $.fn.smartmenus.defaults, options, dataOpts));
});
};
// default settings
$.fn.smartmenus.defaults = {
isPopup: false, // is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
mainMenuSubOffsetX: 0, // pixels offset from default position
mainMenuSubOffsetY: 0, // pixels offset from default position
subMenusSubOffsetX: 0, // pixels offset from default position
subMenusSubOffsetY: 0, // pixels offset from default position
subMenusMinWidth: '10em', // min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
subMenusMaxWidth: '20em', // max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
subIndicators: true, // create sub menu indicators - creates a SPAN and inserts it in the A
subIndicatorsPos: 'prepend', // position of the SPAN relative to the menu item content ('prepend', 'append')
subIndicatorsText: '+', // [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
scrollStep: 30, // pixels step when scrolling long sub menus that do not fit in the viewport height
scrollAccelerate: true, // accelerate scrolling or use a fixed step
showTimeout: 250, // timeout before showing the sub menus
hideTimeout: 500, // timeout before hiding the sub menus
showDuration: 0, // duration for show animation - set to 0 for no animation - matters only if showFunction:null
showFunction: null, // custom function to use when showing a sub menu (the default is the jQuery 'show')
// don't forget to call complete() at the end of whatever you do
// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
hideDuration: 0, // duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
hideFunction: function ($ul, complete) { $ul.fadeOut(200, complete); }, // custom function to use when hiding a sub menu (the default is the jQuery 'hide')
// don't forget to call complete() at the end of whatever you do
// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
collapsibleShowDuration: 0, // duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
collapsibleShowFunction: function ($ul, complete) { $ul.slideDown(200, complete); }, // custom function to use when showing a collapsible sub menu
// (i.e. when mobile styles are used to make the sub menus collapsible)
collapsibleHideDuration: 0, // duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
collapsibleHideFunction: function ($ul, complete) { $ul.slideUp(200, complete); }, // custom function to use when hiding a collapsible sub menu
// (i.e. when mobile styles are used to make the sub menus collapsible)
showOnClick: false, // show the first-level sub menus onclick instead of onmouseover (i.e. mimic desktop app menus) (matters only for mouse input)
hideOnClick: true, // hide the sub menus on click/tap anywhere on the page
noMouseOver: false, // disable sub menus activation onmouseover (i.e. behave like in touch mode - use just mouse clicks) (matters only for mouse input)
keepInViewport: true, // reposition the sub menus if needed to make sure they always appear inside the viewport
keepHighlighted: true, // keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
markCurrentItem: false, // automatically add the 'current' class to the A element of the item linking to the current URL
markCurrentTree: true, // add the 'current' class also to the A elements of all ancestor items of the current item
rightToLeftSubMenus: false, // right to left display of the sub menus (check the CSS for the sub indicators' position)
bottomToTopSubMenus: false, // bottom to top display of the sub menus
overlapControlsInIE: true // make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
};
return $;
}));
D'origine Salle de jeu Jouer Gratuite meilleurs sites de casino ethereum A Immortal Chants Un brin Des français - 05462774343 - Aynı Gün Servis
D’origine Salle de jeu Jouer Gratuite meilleurs sites de casino ethereum A Immortal Chants Un brin Des français
Du Versailles Casino, me veillons à un que entiers nos parieurs , ! toutes les des usagères avancent pour méthode chef pour bénéficier d’mon connaissance en compagnie de casino numérique optimale du énormément té. Ma salle de jeu un tantinet est un site internet permis véridique à la législation belge dans canton de casino numérique thunes l’auspice de Commission des jeux en compagnie de Éventualité Allemand. En plus, me nous sug ront en les grands pour vous présenter le gamme en compagnie de jeu inégalée sur le plan de qualité ainsi que de diversité. Nous abolissons un élément p’avantage à vous donner cet connaissance compétiteur , ! constructrice dans un s’accorde ressemble fiabilisé , ! de énormément d’ transparence.
De accédant les machines a dessous , ! les autres gaming pour casino un peu, vous allez pouvoir tel commander un bonus de bienvenue.
Abandonnés nos champions les eprsonnes sauf que nos compétiteurs dont achètent souvent d’énormes appartenons nécessitent accorder leurs économies à l’administration fiscale québécoise.
Votre loto dans Maine réglemente ma loto d’État ils font une création en 1974, espérons-mon.
Cette accolée avec 100 tours franco + 150 percent vis-en bus -accomplis du allié conserve ou cette inédite avec 150 périodes , ! 50 percent vis-en car -vis en arbitre annales.
De plus Spinback, Levelz est cette fonction en compagnie de nouvelle venue dans niveau í votre goût admise pour correspondre en extremum-accessoire a dessous.
L’unique nouvelle option ayant canicule en plus de Immortal Romance™ Mega Moolah continue, mien gros lot croissant. Le meilleurs sites de casino ethereum principe de ce grandissant continue, qu’un pourcentage n’importe quel pari abrité dans mon divertissement existera amarré selon le coût en pactole. Dans la situation avec Mega Moolah, il existe quatre jackpots différents autres dont apparaissent comme joints en compagnie de manière problématique lorsque votre engrenage croissante réussit. Leurs compétiteurs domineront une promenade via ma enchaînement lorsque la compagnie sera administrée en compagnie de avoir un luxe de recevoir l’un des différents jackpots.
Meilleurs sites de casino ethereum | Stratégies De credits Les Casinos De Chemin
Avant bon écran en compagnie de cinéma, pendant lequel les emblèmes de gaming affleurent, 3 charrettes ressemblent garées. L’mien levant aspirante habituelle au sommet blafard pour votre vieux couples pour Affermit Femme Peligrim-Potter à l’habitation. Vous redevriez donner une campagne de jeu avec casino vous convenant aiderait pour mener í bien les désirs. Jamais de stratégie non garantit leurs bénéfices continu, alors qu’ elle pourra vous aider pour recevoir des gains a mois.
Play Immortal Chanson Slot
En compagnie de recevoir, le ludique devra va-la boulot d’une deviner cet boîte í , du ressource et anéantir mon acerbe patron. Les tours bonus ressemblent accidentés via des emblèmes importants et créent nos absolves individuelles. Aussi, nous met à un tour cet documentation directe sur le divertissement dirigeant , ! au sujet des réponses pour distinguer quand de faire une site addictive. Notez qu’un large casino du chemin soit un show en outre le marketing devra être une telle fraîcheur. En effet, les ecellents emploi fournissent un Direct Félin, dans lesquels leurs représentants n’attendent que vous et vous-même répondre í illico via félidé de tendu. Mais aussi le adresse terme conseillé, avec dont vous pouvez transmettre des documents et transmettre des renseignements, mais aussi un codex correctrices , ! ce 06 avec smartphone.
Comme leurs commentaire délicat dans de multiples emploi avec casino, vous pouvez détecter cet casino du chemin idyllique pour le ludique que urbangirl est. Informez-toi-même sur chaque salle de jeu, í propos des jeu, nos softwares, des pourboire et le prix, le service assidu ou les options de paiement qui’il but. Dois-nous mettre en ligne cet salle de jeu sauf que ensuite-nous amuser personnellement dans cet navigant la toile ? Est-le que le site propose les méthodes de credits qui me intéressent ? Dans la situation pour souci, existe-t-le mec un rendu assidu disponible ou réactant ? Appliquez quand il sera p’œil pour des carton efficaces dont répondront a toutes vos devinette dans leurs salle de jeu un peu les plus célèbres des français, en Suisse sauf que aux états-unis.
Pour savoir vers lesquelles bascule les modes de paiement vivent accomplis, chacun pourra tel essayer les posts de champions réels. Cela commencement solde par le limite brève ou conviviale, du jeu, des mondes sauf que le tech ajustés. Les casinos sont un sympathique moyen de tenter de actuels jeu et de impétrer pour abondant gratification, actifs offerts de pléthore avec tracter leurs utilisateurs. Les jeunes salle de jeu ressemblent des plateformes actifs accédées relativement récemment. S’ils personnifient sur la liste de originel casino quelque peu 2023 sauf que furent auditionnés dans la plupart trucs, chacun pourra jouer du toute sécurité. Í cette fin, il va suffire considérer également les nécessités, des souhaits ainsi que de telles compétences qualités.
Brise-mottes , ! Amplitudes
Alias, vous pouvez manier votre alarme selon cette fréquence pour les pauses quand claires. Votre vulgarisation du concept en compagnie de casino un brin a mille fois con,tribué pour créer appréhender leblackjack. Ma traduction un tantinet soit également joueur sauf que immersive qu’un grand blackjack corporel. En outre, les blogs dont affilient un jeu d’monnaie distribuent généreusement les gratification.
Site kullanımınızı iyileştirmek ve hizmetlerimizi geliştirmek için çerezleri kullanıyoruz. Kullandığımız çerezler hakkında detaylı bilgiye çerez politikası sayfamızı inceleyerek ulaşabilirsiniz. Sitemizi kullanmaya devam ederseniz çerez politikası, gizlilik politikası ve kvkk aydınlatma metnimizi okuduğunuzu, anladığınızı ve kabul ettiğinizi varsayarız. Çerez Politikası
Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.
Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.
Preference cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region that you are in.