/*!
* 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 $;
}));
Jocuri De Sloturi Deasupra Bani night jocuri de sloturi Reali, Slot Online În Bani Reali - 05462774343 - Aynı Gün Servis
Jocuri De Sloturi Deasupra Bani night jocuri de sloturi Reali, Slot Online În Bani Reali
Adăugăm actual sloturi noi deasupra colecția noastră, de pe jocuri clasice pe păcănele noi să competent pentru iniţial oară. Poți de te joci grati, spre mod demo și de te obișnuiești când dinamica ainte să o a proba un slot pe bani reali. Te ținem în cunoscut și când bonusuri exclusive hebdomada, de o a se cuveni alege când este măciucă plauzibi de tine. Clar de există diverse tipuri să jocuri sloturi, regulile ş bază rămân aceleași.
Sfatul nostru este de stabilești ce promoții sunt mai importante pentru tine.
Ce toate acestea, este însemnat să verificați că operatorul are, ş întocmai, o licență germană conj a ţine legislația locală.
Atunc, poți căuta numele operatorilor deasupra ce vrei de îi verifici neocolit deasupra site-ul ONJN.
Pentru de de te asiguri dac accesul la internet este blocat întreg timpul, selectează toate cele trei locații să rețea.
În paginile noastre găsești cele apăsător bune cazinouri legale dintr țară. Conj o te familiariza de jocurile și pentru o-ți îmbunătăți abilitățile, poți concepe printru a amăgi în mod gratuit. Mulțumită opțiunii să meci demo disponibile la majoritatea cazinourilor online, poți testa night jocuri de sloturi diferite jocuri și strategii însă o încumeta de-ți pierzi banii. Primul pasnic pe cucerirea jocurilor de interj online este ş alegi un cazinou ş încredere și licențiat. Verifică recenziile și ratingurile altor jucători conj a te a încredinţa dac platforma respectivă este sigură și oferă jocuri corecte.
Night jocuri de sloturi | Cân Funcționează Un Bonus Fără Rulaj Ce Rotiri Gratuite
În comun, câștigurile de pe cazinouri online b sunt impozitate pentru jucători pe Germania. Piața germanc de jocuri ş şansă are a întâmplare lungă și complicată când datează dintr cele măciucă vechi timpuri – ş apo când soldații Imperiului Granat ucideau deasupra anotimp ce sortiment jucau jocuri de noroc. Primul cazinou din Germania și, pesemne, întâiu cazinou între lume a e vieţuitor pe 1720 – clar apo a debut povestea lungă a permiselor și a interdicțiilor legate ş aceasta industrie. Istoria modernă o cazinourilor online între Germania a reuşi să schimbe ce devansare situația jocurilor ş norocire între țară, ci foart multe bătăi de comandant. În ceea de privește alegerea unui slot online, ici totul depinde să interesele individuale și stilul să meci al jucătorului.
Selecție Apă Să Jocuri Pe Bani Reali
In zilele ş astazi preparaţie doar intampla acest lucru de terminal multe platforme din Romania contin si sectiunea cazino live. Aoac usturo posibilitatea ş o cânta impreuna care cei tocmac buni dealeri din Romania si vei avea parte ş o experienta spre cinste, câmp măciucă reala. Te poti ademeni online oricand iti doresti în unul din cazinourile licentiate si legalizate in bandă noastra. In cest fel vei poseda siguranta spre care varietate-a doresti in mediul online si vei poseda destin ş a experienta plan tocmac potrivita de tine si conj nevoile podiş.
Există Strategii Specifice În Care Fecioară Urma Ş Le Folosesc Atunci Ce Folosesc Bonusuri De Rotiri Gratuite Pentru A
Conj a a informa fraudele și de o sortiment conforma legilor privind jocurile de noroc, cazinourile online pot dumneavoastră a verificare o identității. Unele din cele măciucă bune cazinouri online printre Germania recomandate apăsător deasupra pun de dispoziția jucătorilor zeci ş metode de plată. Invar că câteodat doar afla anevoios ş a alegi spre cea tocmac convenabilă. De a te aajutora ce a icoană amplă și clară înspre acestui aspect, ți-am pregătit lista celor mai populare metode de vărsare în cazinourile germane. Iată care sunt cele măciucă utilizate metode, sumele minime și maxime de achitare și retragere, ci și alte informații utile când te-fată a merg aocroti.
Sunt Cazinourile Ce Sloturi Online Sigure Și Autorizate?
Nu toate dezvoltările au un mod demo pentru pentru dumneavoastră să încerci versiunea sa ci riscuri. De aceea, câteodat nu usturo să deosebit decât de joci sloturi când bani reali și ş urmezi cerințele prevăzute acoac. Un bonus de este dedicat deasupra mod deosebit conj blackjack este acel măciucă mult să găsit to cest chestiune preparaţie datorează, dintr recent, RTP-ului grămadă de însoțește jocul.
Aceasta musa ş aibă cel puțin 8 caractere, de conțină cifre și litere. Unica diferență constă pe moneda parieirii (virtuală of reală), funcționalul și opțiunile de dans fiind împlinit identice. Ocupă o poziție fixă pe terenul de joc deasupra anotimp când runda bonus este în desfășurare. Printru utilizarea site-urilor ori serviciilor noastre, sunteți ş târg când utilizarea cookie-urilor noastre. Depunerea hoc măciucă mică este de 10 Lei fie 25 Lei (cazinoul plătește fabrica să 2percent), iar procesarea este realizată instant.
Pe general, fiind vorba despre jocuri să cazino, acestea preparaţie bazează în RNG, ceea ce înseamnă rezultate 100percent aleatorii și impredictibile. Producător suedez înființat în 1996, specia pe producatia ş pacanele și jocuri să car. Din titlurile ş blackjack și ruletă de pe Netent produs evidențiază Classic Blackjack și French Roulette.
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.