/* Pagelayer Pen editor */ var pagelayer_customColor = ["#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff", "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff", "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff", "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2", "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466"]; var pagelayer_pen_sizeList = ['normal', 'x-small', 'small', 'medium', 'large', 'x-large']; var pagelayer_pen_lineHeight = ['0.9', '1', '1.5', '2.0', '2.5','3.0', '3.5', '4.0', '4.5', '5.0']; class PagelayerPen{ constructor(jEle, options) { var t = this; t.editor = jQuery(jEle); t.options = options; // Get the document of the element. It use to makes the plugin // compatible on iframes. t.doc = jEle.ownerDocument || document; t.tagToButton = {}; t.optionsCounter = 0; t.destroyEd = true; t.semantic = null; t.DEFAULT_SEMANTIC_MAP = { 'b': 'strong', 'i': 'em', 's': 'strike', //'strike': 'del', 'div': 'p' }; // Init editor t.addHandlers(); t.init(); } init(){ var t = this; // Init Editor t.editor.addClass('pagelayer-pen'); t.penHolder = t.addContainer(); t.addEvents(); } addHandlers(){ // TODO : Add for custom plugins // TODO remove all execCommands this.handlers = { bold:{ tag: 'STRONG', icon: '' }, italic:{ tag: 'EM', icon: '' }, underline:{ tag: 'U', icon: '' }, strike:{ tag: 'strike', fn: 'strikethrough', icon: '' }, h1:{ fn: 'formatBlock', icon: 'H1' }, h2:{ fn: 'formatBlock', icon: 'H2' }, h3:{ fn: 'formatBlock', icon: 'H3' }, h4:{ fn: 'formatBlock', icon: 'H4' }, h5:{ fn: 'formatBlock', icon: 'H5' }, h6:{ fn: 'formatBlock', icon: 'H6' }, p:{ fn: 'formatBlock', icon: '' }, blockquote:{ fn: 'formatBlock', icon: '' }, formating:{ fn: 'formatBlock', fixIcon: '' }, unorderedlist:{ tag: 'UL', fn: 'insertUnorderedList', icon: '' }, orderedlist:{ tag: 'OL', fn: 'insertOrderedList', icon: '' }, sub:{ tag: 'sub', fn: 'subscript', icon: '' }, super:{ tag: 'sup', fn: 'superscript', icon: '' }, link:{ fn: 'setLinkHandler', tag: 'a', icon: '', }, image:{ fn: 'imageBtnHandler', icon: '' }, align:{ style: 'text-align', fn: 'formatBlock', icon: { 'left': '', 'center': '', 'right': '', 'justify': '', } }, color:{ class: 'pagelayer-pen-color-picker', style: 'color', fn: 'commandHandler', fixIcon: ' ', buildBtn : 'buildColorBtnHandler', default : pagelayer_customColor, customInpute: true }, background:{ class: 'pagelayer-pen-color-picker', style: 'background-color', fn: 'commandHandler', fixIcon: ' ', buildBtn: 'buildColorBtnHandler', default : pagelayer_customColor, customInpute: true }, size:{ class: 'pagelayer-pen-size-picker', style: 'font-size', fn: 'commandHandler', default : pagelayer_pen_sizeList, customInpute: true }, lineheight:{ style: 'line-height', fn: 'commandHandler', fixIcon: '', default : pagelayer_pen_lineHeight, customInpute: true }, font:{ style: 'font-family', fn: 'commandHandler', fixIcon: '', default : pagelayer_fonts, buildBtn : 'buildfontBtnHandler', }, viewHTML:{ fn: 'viewHTMLBtnHandler', icon: '' }, removeformat:{ icon: '' } } } addContainer(className){ className = className || false; // Add Container var container = jQuery('.pagelayer-pen-holder'); if(container.length < 1){ jQuery('body').append('
'); container = jQuery('.pagelayer-pen-holder'); } if(!className){ return container; } if(container.find('.'+className).length < 1){ container.append('
'); } return container.find('.'+className); } addToolbar(){ // Add Toolbar var t = this; var groups = t.options.toolbar; var toolbar = t.toolbar = t.addContainer('pagelayer-pen-toolbar'); // Make it empty toolbar.empty(); if (!Array.isArray(groups[0])) { groups = [groups]; } var addButton = function(container, format, value){ var btn = t.handlers[format]; var icon = ''; if('icon' in btn){ var _icon = btn['icon']; if(typeof _icon == 'object' && !pagelayer_empty(_icon[value])){ icon = _icon[value]; }else if(typeof icon == 'string'){ icon = _icon; } } var input = document.createElement('button'); input.setAttribute('type', 'button'); input.setAttribute('data-format', format); input.classList.add('pagelayer-pen-' + format); if('class' in btn){ input.classList.add(btn['class']); } if( pagelayer_empty(value) && 'default' in btn ){ value = btn['default']; } input.innerHTML = icon; if(value != null) { input.value = value; } container.appendChild(input); } var createoption = function(val, lang, type){ type = type || ''; var lang = pagelayer_empty(lang) ? 'Default' : lang; return ''; } var addSelect = function(container, format, values) { var input = document.createElement('select'); input.classList.add('pagelayer-pen-' + format); if('class' in t.handlers[format]){ input.classList.add(t.handlers[format]['class']); } input.setAttribute('data-format', format); if( pagelayer_empty(values) && 'default' in t.handlers[format] ){ values = t.handlers[format]['default']; } for(var kk in values){ var options = ''; var value = values[kk]; if(typeof value == 'object') { if(kk != 'default'){ options += ''; } for(y in value){ options += createoption((jQuery.isNumeric(y) ? value[y] : x), value[y], kk); } }else if(value !== false) { options += createoption(value, value); } else { options += createoption('', ''); } jQuery(input).append(options); } container.appendChild(input); } groups.forEach(function(controls){ var group = document.createElement('span'); group.classList.add('pagelayer-pen-formats'); controls.forEach(function (control){ var format = control; if(typeof control === 'object'){ format = Object.keys(control)[0]; } if( pagelayer_empty(t.handlers[format]) ){ return; } if( typeof control === 'string' ){ addButton(group, control); } else { var value = control[format]; if (Array.isArray(value)) { addSelect(group, format, value); } else { addButton(group, format, value); } } var btn = t.handlers[format]; t.tagToButton[(btn.tag || btn.style || format).toLowerCase()] = format; }); // TODO skip if format is not exist toolbar[0].appendChild(group); }); toolbar.find('button').on('click', function(){ var bEle = jQuery(this); var format = bEle.data('format'); if(! format in t.handlers){ return; } var btn = t.handlers[format]; t.currentFormat = format; t.execCmd(btn.fn || format, btn.param || format, btn.forceCss); }); toolbar.find('select').on('change', function(e){ var bEle = jQuery(this); var format = bEle.data('format'); var val = bEle.val(); if(! format in t.handlers){ return; } var btn = t.handlers[format]; t.currentFormat = format; t.execCmd(btn.fn || format, val, btn.forceCss); }); toolbar.find('select').each(function(){ var format = jQuery(this).data('format'); if('buildBtn' in t.handlers[format]){ try{ t[t.handlers[format]['buildBtn']](this); }catch(e){ try{ t.handlers[format]['buildBtn'](this); }catch(e2){ t.buildDropdown(this); } } return true; } t.buildDropdown(this); }); // Add close button toolbar.append(''); // Hide editor on click close tool handler toolbar.find('.pagelayer-pen-close').on('mousedown', function(e){ //e.preventDefault(); t.destroyEd = true; t.editor.trigger('blur'); }); } execCmd(cmd, param, forceCss, skipPen){ var t = this; skipPen = !!skipPen || ''; if(cmd !== 'dropdown'){ t.focus(); t.restoreRange(); } try{ document.execCommand('styleWithCSS', false, forceCss || false); }catch(c){} try{ t[cmd + skipPen](param); }catch(c){ try{ cmd(param); }catch(e2){ if(cmd === 'insertHorizontalRule'){ param = undefined; }else if (cmd === 'formatBlock'){ // TODO: check for && t.isIE param = '<' + param + '>'; } document.execCommand(cmd, false, param); t.semanticCode(); t.restoreRange(); } } if(cmd !== 'dropdown'){ t.updateButtonStatus(); t.editor.trigger('input'); } } commandHandler(value){ var t = this; var format = t.currentFormat; if( pagelayer_empty(format) ){ return; } var btn = t.handlers[format]; var sel = window.getSelection(); var text = t.range.commonAncestorContainer; var selectedText = t.range.cloneContents(); selectedText = jQuery('
').append(selectedText).html(); // Also select the tag if(text.nodeType === Node.TEXT_NODE){ text = text.parentNode; } if (text.innerHTML === selectedText && text != t.editor[0]) { var ele = jQuery(text); if('tag' in btn){ // Replace tag }else if('style' in btn){ var style = {}; style[btn.style] = value; ele.css(style); }else if('atts' in btn){ // Add attribute or toggle the element } } else { // TODO for toggle tags and add tags var html = jQuery('' + selectedText + ''); // Remove style from all childrend var style = {}; style[btn.style] = ''; html.find('[style]').css(style); // TODO: remove span element that have no atts var node = html[0]; var firstInsertedNode = node.firstChild; var lastInsertedNode = node.lastChild; t.range.deleteContents(); t.range.insertNode(node); if(firstInsertedNode) { t.range.setStartBefore(firstInsertedNode); t.range.setEndAfter(lastInsertedNode); } // Is previous element empty? var prev = jQuery(node).prev(); if( prev.length > 0 && prev.is(':empty') ){ prev.remove(); } } sel.removeAllRanges(); sel.addRange(t.range); } formatBlock(value){ var t = this, format = t.currentFormat, btn = t.handlers[format], startNode = t.range.startContainer, endNode = t.range.endContainer; if( startNode.nodeType == Node.TEXT_NODE && startNode.parentNode != t.editor[0] ){ startNode = startNode.parentNode; } if( endNode.nodeType == Node.TEXT_NODE && endNode.parentNode != t.editor[0] ){ endNode = endNode.parentNode; } // TODO: only for seleced content // Wrap text nodes in span for easier processing t.editor.contents().filter(function () { return this.nodeType === 3 && this.nodeValue.trim().length > 0; }).wrap(''); var isLineEnd = function(lEle){ return lEle == null || lEle.nodeName == 'BR' || t.isline(lEle); } var wrapLine = function(pLine){ var pLine = jQuery(pLine), lineFele, lineEele, finalP; // Get Parent Element if(pLine.parentsUntil(t.editor).length > 0){ pLine = pLine.parentsUntil(t.editor).last(); } if(t.isline(pLine)){ return pLine; } // Get line first element if(isLineEnd(pLine[0].previousSibling)){ lineFele = pLine; }else{ lineFele = pLine.prevAll().filter(function(){ return isLineEnd(this.previousSibling); }).first(); } // Get line last element if(isLineEnd(lineFele[0].nextSibling)){ lineEele = lineFele; }else{ lineEele = lineFele.nextAll().filter(function(){ return isLineEnd(this.nextSibling); }).first(); } // Wrap all with p tag if(lineFele.is(lineEele)){ finalP = lineFele.wrap('

').parent() }else{ finalP = lineFele.nextUntil(lineEele.next()).addBack().wrapAll('

').parent(); } finalP.next('br').remove(); return finalP; } // Get start block lavel elements var $sNode = jQuery(t.blockNode(startNode)); if($sNode.is(t.editor)){ $sNode = wrapLine(startNode); } var $eNode = jQuery(t.blockNode(endNode)); if($eNode.is(t.editor)){ $eNode = wrapLine(endNode); } var $oldEle = $sNode; if(! $sNode.is($eNode) ){ var findEnd = false; var addElement = function(addEle){ if(addEle[0].nodeName == 'UL' || addEle[0].nodeName == 'OL') { addEle.children().each(function(){ $oldEle = $oldEle.add(jQuery(this)); }); return; } $oldEle = $oldEle.add(addEle); } var wrapAllEle = function(nextEle){ if(nextEle.is($eNode) || nextEle.find($eNode).length > 0){ findEnd = true; return; } if(nextEle.length < 1){ return; } if(!t.isline(nextEle[0])){ nextEle = wrapLine(nextEle); } addElement(nextEle); wrapAllEle( nextEle.next() ); } wrapAllEle($sNode.next()); // Is start Element have a another parent var pars = $sNode.parentsUntil(t.editor); pars.each(function(){ var $par = jQuery(this); wrapAllEle($par.next()); }); if( pars.length > 0 ){ $sNode = pars.last(); } var nextEnd = $sNode.nextAll().filter(function(){ return jQuery(this).is($eNode) || jQuery(this).find($eNode).length > 0; }).first(); // Add elements if( nextEnd.length > 0 ){ var $nextEle = $sNode.nextUntil(nextEnd); $nextEle.each(function(){ var ulEle = jQuery(this); if($oldEle.has(ulEle)) return; addElement(ulEle); }); } // Add end element if(nextEnd.length > 0 && !nextEnd.is($eNode) && (nextEnd[0].nodeName == 'UL' || nextEnd[0].nodeName == 'OL')){ nextEnd.children().each(function(){ var li = jQuery(this); $oldEle = $oldEle.add(li); if(li.is($eNode) || li.find($eNode).length > 0) return false; }); }else{ $oldEle = $oldEle.add($eNode); } } if('style' in btn){ var style = {}; style[btn.style] = value; $oldEle.css(style); }else if('atts' in btn){ // Add attribute or toggle the element var attr = {}; attr[btn.atts] = value; $oldEle.attr(attr); }else{ // Replace tag var tag = value.toLowerCase(); // need to find all block ele and replace this $oldEle.each( function(){ var $cEle = jQuery(this); if($cEle.is(t.editor)){ return; } // Is List element if($cEle.css('display') == 'list-item'){ if( t.isline($cEle[0].firstChild)){ $cEle.children().each(function(){ var liChild = jQuery(this); if(t.isline(liChild[0])){ t.replaceTag(liChild, tag, true); return; } // TODO: Check and need to correct liChild.wrap('<' + tag + '/>'); liChild.next('br').remove(); }); return } $cEle.contents().wrapAll('<' + tag + '/>'); return; } t.replaceTag($cEle, tag, true); }); } // Get rid of pen temporary span's jQuery('[data-pts]', t.editor).contents().unwrap(); t.semanticCode(); t.restoreRange(); } blockNode( node ){ var t = this; while( !t.isline(node) && node != t.editor[0] ) { node = node.parentNode; } return node; } isline(node){ if (node.nodeType !== Node.ELEMENT_NODE) return false; if (node.childNodes.length === 0) return false; // Exclude embed blocks var style = window.getComputedStyle(node); return ['block', 'list-item'].indexOf(style.display) > -1; } replaceTag(ele, tag, copyAttr){ ele.wrap('<' + tag + '/>'); var par = ele.parent(); if(copyAttr){ jQuery.each(ele.prop('attributes'), function () { par.attr(this.name, this.value); }); } ele.contents().unwrap(); return par; } semanticCode(){ var t = this; t.semanticTag('b'); t.semanticTag('i'); t.semanticTag('s'); t.semanticTag('strike'); t.semanticTag('div', true); } semanticTag(oldTag, copyAttributes){ var t = this; var newTag; if(t.semantic != null && typeof t.semantic === 'object' && t.semantic.hasOwnProperty(oldTag)){ newTag = t.semantic[oldTag]; } else if (t.DEFAULT_SEMANTIC_MAP.hasOwnProperty(oldTag)) { newTag = t.DEFAULT_SEMANTIC_MAP[oldTag]; } else { return; } jQuery(oldTag, t.editor).each(function () { var $oldTag = jQuery(this); if($oldTag.contents().length === 0) { return false; } t.replaceTag($oldTag, newTag, copyAttributes); }); } addEvents(){ // Add Events var t = this, editor = t.editor, ctrl = false, debounceButtonStatus; var showToolBar = function(){ var jEle = t.penHolder.children(':visible'); if(jEle.length < 1){ jEle = t.toolbar; } t.showPen(jEle); }; // Save rage editor.on('focusout', function(e){ if(t.destroyEd){ t.editor.removeClass('pagelayer-pen-focused'); t.range = null; return; } t.saveRange(); }); // Prevent to hide toolbar t.penHolder.on('mousedown', function(e){ // TODO: taget only require Element t.destroyEd = false; }); // On editor blur editor.on('blur', function(){ if(!t.destroyEd){ return; } t.destroy(); }); editor.on('keydown', function(){ t.penHolder.hide(); }); editor.on('mousedown', function(){ if(t.editor.attr('contenteditable') == 'true'){ t.showPen(); } }); editor.on('mouseup keyup keydown', function(e){ if ((!e.ctrlKey && !e.metaKey) || e.altKey) { setTimeout(function () { // "hold on" to the ctrl key for 50ms ctrl = false; }, 50); } clearTimeout(debounceButtonStatus); debounceButtonStatus = setTimeout(function () { t.updateButtonStatus(); }, 50); }); // Set focus on editor editor.on('click', function(e){ if(t.editor.hasClass('pagelayer-pen-focused')){ return; } t.editor.attr('contenteditable', 'true'); t.editor.focus(); }); // Set focus on editor editor.on('focus', function(){ t.destroyEd = true; t.addToolbar(); t.showPen(); t.editor.addClass('pagelayer-pen-focused'); jQuery(window).unbind('scroll.penToobar'); jQuery(window).on('scroll.penToobar', showToolBar); jQuery(document).unbind('mousemove.penToobar'); jQuery(document).on('mousemove.penToobar', showToolBar); }); t.semanticCode(); } destroy(){ var t = this; //t.editor.attr('contenteditable', ''); t.penHolder.hide(); // Removing event listeners jQuery(document).unbind('mousemove.penToobar'); jQuery(window).unbind('scroll.penToobar'); } hasFocus(){ var t = this; return ( t.doc.activeElement === t.editor || t.contains( t.editor[0], t.doc.activeElement) ); } contains(parent, descendant) { try { // Firefox inserts inaccessible nodes around video elements descendant.parentNode; // eslint-disable-line no-unused-expressions } catch (e) { return false; } return parent.contains(descendant); } saveRange(){ var t = this, selection = t.doc.getSelection(); t.range = null; if (!selection || !selection.rangeCount) { return; } var savedRange = t.range = selection.getRangeAt(0), range = t.doc.createRange(), rangeStart; range.selectNodeContents(t.editor[0]); range.setEnd(savedRange.startContainer, savedRange.startOffset); rangeStart = (range + '').length; t.metaRange = { start: rangeStart, end: rangeStart + (savedRange + '').length }; } restoreRange(){ var t = this, metaRange = t.metaRange, savedRange = t.range, selection = t.doc.getSelection(), range; if(!savedRange){ return; } if(metaRange && metaRange.start !== metaRange.end){ // Algorithm from http://jsfiddle.net/WeWy7/3/ var charIndex = 0, nodeStack = [t.editor[0]], node, foundStart = false, stop = false; range = t.doc.createRange(); while(!stop && (node = nodeStack.pop())){ if (node.nodeType === 3){ var nextCharIndex = charIndex + node.length; if (!foundStart && metaRange.start >= charIndex && metaRange.start <= nextCharIndex) { range.setStart(node, metaRange.start - charIndex); foundStart = true; } if (foundStart && metaRange.end >= charIndex && metaRange.end <= nextCharIndex) { range.setEnd(node, metaRange.end - charIndex); stop = true; } charIndex = nextCharIndex; } else { var cn = node.childNodes, i = cn.length; while (i > 0) { i -= 1; nodeStack.push(cn[i]); } } } } selection.removeAllRanges(); selection.addRange(range || savedRange); } getRange(){ var t = this; var selection = t.doc.getSelection(); if (selection == null || selection.rangeCount <= 0) return null; var range = selection.getRangeAt(0); if(range == null) return null; return range; } getRangeText(range){ return range + ''; } focus(){ var t = this; if(t.hasFocus()) return; t.editor.click(); t.editor.focus(); t.restoreRange(); } getBounds(range){ var rect = range.getBoundingClientRect(); return { bottom: rect.top + rect.height, height: rect.height, left: rect.left, right: rect.right, top: rect.top, width: 0 }; } showPen(jEle){ var t = this; jEle = jEle || jQuery(t.toolbar); var toolBar = jQuery(t.penHolder); var tooltipHeight = parseInt(toolBar.css('height')); var range = null; if(! t.hasFocus() && t.range != null){ range = t.range; }else{ range = t.getRange(); } if(range == null){ toolBar.hide(); return; } // Set left of toolbar var editorOffset = t.editor[0].getBoundingClientRect(); var editorTop = editorOffset.top; var editorLeft = editorOffset.left; var editorbottom = editorTop + editorOffset.height - tooltipHeight; var toolBarTop = editorTop - 10; var bound = t.getBounds(range); if(bound.height == 0 && bound.top == 0 && bound.left == 0){ toolBar.hide(); return; } var boundTop = bound.top - 15; // Set top of toolbar if( boundTop - tooltipHeight < 0 && bound.bottom > -5){ toolBarTop = bound.bottom + tooltipHeight + 15; }else if( editorbottom - 30 < 0 ){ toolBarTop = editorbottom + 20; }else if( toolBarTop - tooltipHeight < 0 ){ toolBarTop = tooltipHeight + 10; } // Show Toolbar toolBar.children().hide(); toolBar.show(); jEle.show(); // Set top of toolbar toolBar.css('top', toolBarTop); // Set left of toobar var docW = jQuery(window).width() - 10; var toolW = toolBar.width(); var edW = t.editor.width(); if(toolW > edW){ editorLeft = editorLeft - (toolW - edW) / 2 } toolBar.css('left', editorLeft+'px'); var tooltipLeft = toolBar.offset().left; if(tooltipLeft < 0){ toolBar.css('left', '1px'); } var toolRight = tooltipLeft + toolW; if(docW < toolRight){ toolBar.css('left', tooltipLeft - (toolRight - docW)+'px'); } } getContent(){ var editor = this.editor; var html = editor.html(); return html; } setContent(html){ var t = this; html = html || ''; t.editor.html(html); t.editor.trigger('input'); } updateButtonStatus(){ var t = this, toolbar = jQuery(t.toolbar), tags = t.getTagsRecursive(t.doc.getSelection().focusNode), activeClasses = 'pagelayer-pen-active'; jQuery('.' + activeClasses, toolbar).removeClass(activeClasses); jQuery.each(tags, function (i, tag){ var btnName; if(pagelayer_is_string(tag)){ btnName = t.tagToButton[tag.toLowerCase()]; }else{ btnName = t.tagToButton[Object.keys(tag)[0].toLowerCase()] } var $btn = jQuery('[data-format="'+btnName+'"]', toolbar); if($btn.length < 1){ return; } if($btn.find('.pagelayer-pen-picker-label').length > 0){ $btn.find('.pagelayer-pen-picker-label').addClass(activeClasses); return; } $btn.addClass(activeClasses); }); } getTagsRecursive(element, tags) { var t = this; var jEle = jQuery(element); tags = tags || (element && element.tagName ? [element.tagName] : []); if (element && element.parentNode) { element = element.parentNode; } else { return tags; } var tag = element.tagName; // Is this editor if (tag === 'DIV') { return tags; } // TODO: for all block element if (tag === 'P' && element.style.textAlign !== '') { tags.push(element.style.textAlign); } jQuery.each(t.tagHandlers, function (i, tagHandler) { tags = tags.concat(tagHandler(element, t)); }); tags.push(tag); var styles = jEle.attr('style'); if(!pagelayer_empty(styles)){ var styles = styles.split(';'); jQuery.each(styles, function(i, style){ style = style.split(':'); var ss = String(style[0]).trim(); var vv = String(style[1]).trim(); if(pagelayer_empty(ss) || ss in tags && !pagelayer_empty(tags[ss])){ return; } var obj = {}; obj[ss] = vv; tags.push(obj); }); } return t.getTagsRecursive(element, tags).filter(function (tag) { return tag != null; }); } buildDropdown(select){ var t = this; var fixIcon = ''; select = jQuery(select); var format = select.data('format'); var selAtts = ''; var options = ''; var optId = `pagelayer-pen-picker-options-${t.optionsCounter}`; t.optionsCounter += 1; Array.from(select[0].attributes).forEach(item => { selAtts += ' '+item.name+'="'+ item.value +'"'; }); Array.from(select[0].options).forEach(option => { var attrs = ''; var val = ''; var itemInner = ''; if(option.hasAttribute('value')){ val = option.getAttribute('value'); attrs += ' data-value="'+val+'"'; } if(option.textContent){ attrs += ' data-label="'+option.textContent+'"'; } // Set icon if('icon' in t.handlers[format] && typeof t.handlers[format]['icon'] == 'object' && !pagelayer_empty(t.handlers[format]['icon'][val])){ itemInner = t.handlers[format]['icon'][val]; } options += `${itemInner}`; }); if('fixIcon' in t.handlers[format]){ fixIcon = t.handlers[format]['fixIcon']; } var customInpute = ''; if('customInpute' in t.handlers[format] && !pagelayer_empty(t.handlers[format]['customInpute'])){ customInpute = ''; } var container = jQuery(` `); container.addClass('pagelayer-pen-picker'); select.before(container); select.hide(); var close = function(cEle){ cEle.removeClass('pagelayer-pen-expanded'); cEle.find('.pagelayer-pen-picker-label').attr('aria-expanded', 'false'); cEle.find('.pagelayer-pen-picker-options').attr('aria-hidden', 'true'); } var selectItem = function(item, trigger = false){ var selected = container.find('.pagelayer-pen-selected'); var label = container.find('.pagelayer-pen-picker-label'); var val = ''; if (item === selected) return; if (selected != null) { selected.removeClass('pagelayer-pen-selected'); } if(item == null) return; item.classList.add('pagelayer-pen-selected'); select.selectedIndex = Array.from(item.parentNode.children).indexOf( item, ); if (item.hasAttribute('data-value')) { val = item.getAttribute('data-value'); label.attr('data-value', val); } else { label.attr('data-value', val); } if (item.hasAttribute('data-label')) { label.attr('data-label', item.getAttribute('data-label')); } else { label.attr('data-label', ''); } if(!fixIcon){ label.html(item.innerHTML); } if(trigger) { select.val(val); select.trigger('change'); close(container); } } var toggleAriaAttribute = function(element, attribute) { element.setAttribute( attribute, !(element.getAttribute(attribute) === 'true'), ); } var togglePicker = function() { container.toggleClass('pagelayer-pen-expanded'); // Toggle aria-expanded and aria-hidden to make the picker accessible toggleAriaAttribute(container.find('.pagelayer-pen-picker-label')[0], 'aria-expanded'); toggleAriaAttribute(container.find('.pagelayer-pen-picker-options')[0], 'aria-hidden'); } container.find('.pagelayer-pen-picker-item').on('click', function(){ selectItem(this, true); close(container); }); container.find('.pagelayer-pen-picker-label').on('click', function(){ togglePicker(); }); container.find('.pagelayer-pen-custom-input').on('focusout keydown', function(e){ if(e.type == 'keydown' && e.keyCode != 13){ return; } e.preventDefault(); var val = jQuery(this).val(); if(pagelayer_empty(val)){ return; } var opt = select.find('option.pagelayer-pen-custom-value'); if(opt.length < 1){ select.append(''); opt = select.find('option.pagelayer-pen-custom-value'); } opt.val(val); select.val(val); select.trigger('change'); close(container); }); jQuery(t.toolbar).on('mousedown', function(e){ var tEle = jQuery(this); var target = jQuery(e.target); var tPicker = target.closest('.pagelayer-pen-picker'); if(target.closest('.pagelayer-pen-picker-item').length > 0) return; tEle.find('.pagelayer-pen-picker.pagelayer-pen-expanded').each(function(){ var picker = jQuery(this); if(tPicker.length > 0 && tPicker.is(picker))return; close(picker); }); }); // TODO need to correct this function update the select container.on('update', function(){ var item = container.find('.pagelayer-pen-selected'); if(item.length < 1){ item = container.find('.pagelayer-pen-picker-item').first(); } selectItem(item[0]); }); container.trigger('update'); return container; } buildColorBtnHandler(item){ var t = this; var select = t.buildDropdown(item); var format = select.data('format'); // Set color select.find('.pagelayer-pen-picker-item').each(function(){ var opt = jQuery(this); var color = opt.data('value'); opt.css({'background': color}); // TODO remove this and add on selecttion opt.on('click', function(){ if(format == 'color'){ opt.closest('.pagelayer-pen-picker-label').css({'text-color': color}); }else{ opt.closest('.pagelayer-pen-picker-label').css({'background-color': color}); } }); }); } buildfontBtnHandler(item){ var t = this; var select = t.buildDropdown(item); jQuery(item).on('change', function(){ pagelayer_link_font_family(jQuery(this)); }); } setLinkHandler(){ var t = this, documentSelection = t.doc.getSelection(), node = documentSelection.focusNode, text = new XMLSerializer().serializeToString(documentSelection.getRangeAt(0).cloneContents()), url = '', linkBtn = 'Link', unlinkBtn = 'Cancel'; while (['A', 'DIV'].indexOf(node.nodeName) < 0) { node = node.parentNode; } if(node && node.nodeName === 'A'){ var $a = jQuery(node); url = $a.attr('href'); } if(!pagelayer_empty(url)){ linkBtn = 'Update'; unlinkBtn = 'Unlink'; } t.saveRange(); var tooltip = this.addContainer('pagelayer-pen-link-tooltip'); t.linkTooltip = tooltip; var html = ''+linkBtn+''+unlinkBtn+''; tooltip.html(html); var input = tooltip.find('input[name="url"]'); // Keep saving old range var metaRange = t.metaRange; var savedRange = t.range; var restoreRange = function(){ t.metaRange = metaRange; t.range = savedRange; t.restoreRange(); } t.linkTooltip.find('.pagelayer-pen-link-btn').on('click', function(){ var url = input.val(); restoreRange(); t.execCmd('createLink', url, true ); t.editor.trigger('input'); t.showPen(); }); t.linkTooltip.find('.pagelayer-pen-unlink-btn').on('click', function(){ restoreRange(); if(unlinkBtn == 'Unlink'){ t.execCmd('unlink', undefined, undefined, true); } t.showPen(); }); t.showPen(t.linkTooltip); } imageBtnHandler(){ var t = this; t.destroyEd = false; t.destroy(); var frame = pagelayer_select_frame('image'); // On select update the stuff frame.on({'select': function(){ var state = frame.state(); var url = '', alt = '', id = ''; // External URL if('props' in state){ url = state.props.attributes.url; alt = state.props.attributes.alt; // Internal from gallery }else{ var attachment = frame.state().get('selection').first().toJSON(); //console.log(attachment); // Set the new and URL url = attachment.url; alt = attachment.alt; id = attachment.id; } t.editor.click(); t.restoreRange(); t.execCmd('insertImage', url, false, true); var $img = jQuery('img[src="' + url + '"]:not([alt])', t.editor); $img.attr('alt', alt); $img.attr('pl-media-id', id); } }); frame.open(); } viewHTMLBtnHandler(param){ var t = this; var html = t.getContent(); t.destroyEd = false; t.destroy(); // Add Container var HTMLviewer = jQuery('.pagelayer-pen-html-viewer'); if(HTMLviewer.length < 1){ jQuery('body').append('

'+ '
'+ ''+ '
'+ ''+ ''+ '
'+ '
'+ '
'); HTMLviewer = jQuery('.pagelayer-pen-html-viewer'); } HTMLviewer.find('.pagelayer-pen-html-area').val(html); HTMLviewer.show(); HTMLviewer.find('.pagelayer-pen-html-btn-update').unbind('click'); HTMLviewer.find('.pagelayer-pen-html-btn-update').on('click', function(){ var html = HTMLviewer.find('.pagelayer-pen-html-area').val(); t.range = null; t.editor.click(); t.setContent(html); t.editor.trigger('focus'); HTMLviewer.hide(); }); HTMLviewer.find('.pagelayer-pen-html-btn-cancel').unbind('click'); HTMLviewer.find('.pagelayer-pen-html-btn-cancel').on('click', function(){ t.editor.click(); t.focus(); HTMLviewer.hide(); }); } } Казино в интернете: как начать игру в интернете в автоматы - Law Analysis with Rahul

Казино в интернете: как начать игру в интернете в автоматы

Казино в интернете: как начать игру в интернете в автоматы

Стремительное и непрекращающееся развитие интернет-азартных игр привело к тому, что рядовому игроку теперь нет необходимости посещать наземные казино и залы игровых автоматов. Доступиться к вход к популярным слотам теперь доступно буквально в несколько кликов на каждых актуальных гаджетах и ОС. Интернет игорные заведения с бонусами на данный момент дают возможность наслаждаться новые игры и эксклюзивы, автоматы в ретро-стиле и топовые слоты разнообразных провайдеров. Потенциальным клиентам предоставляется основной набор условий, главное из них – это достигший совершеннолетия период жизни.

В онлайн казино установлены огромное количество аппаратов от известных и уважаемых разработчиков. В каталог азартных развлечений этого онлайн казино включены не только слоты, но и многообразные варианты рулетки, двадцати одного, баккары, покера, в том числе и те, где посетитель соревнуется с реальными крупье (лайв-казино). Достаточно в ассортименте и быстрых игровых автоматов с весьма активным геймплеем, краткими раундами. Такие сайты предоставляют шанс зарабатывать деньги в течение небольшой промежуток времени и без значительных вложений, если знаете, как контролировать риски и следовать своей стратегии, не теряя контроль.

Ведущие казино создают смартфонные модификации сайтов и аппы для обладателей смартфонов с функциональным набором функций, например, пуш-уведомлениями и системой автоматического обхода блокировок. Всякий пользователь способен вступать в акциях, включать промокоды и бонусы, конкурировать с другими пользователями в рамках турниров и лотерейных игр. В определённых онлайн гэмблинг-платформах даже доступны пари на спорт и киберспорт с дополнительными поощрениями для беттеров: фрибетами, приветственными подарками, страхованием ставок. Тестировать делать деньги на пари и игровых машинах в онлайне действительно является удобно, увлекательно и достаточно легко. Невысокий порог старта и минимальное объем необходимых операций для запуска геймплея – это лучшие тому свидетельства.

Форматы работы игровых автоматов в виртуальных казино

Основное разница и плюс виртуальных площадок гэмблинг-сайтов над конкурентами, работающими вживую, выражается в том, что первые предлагают новичкам шанс сыграть безвозмездно. Учетная запись для данного тоже не потребуется на начальном этапе. У любых слотовых автоматов из перечня есть демонстрационные версии, предназначенные для тестирования понравившихся машин и знакомства с ассортиментом в целом. Эти функционируют следующим образом, что геймер делает ставки цифровыми деньгами, некоторое количество которой приобретает после запуска слота. Эта постепенно тратится, но и начисляется при создании призовых цепочек иконок. Для того чтобы полностью пополнить баланс тестовых фишек, необходимо обновить вкладку со слотом.

Следует подчеркнуть, что демки игровых автоматов в интернет-казино почти не отличаются от своих оригинальных версий. В них совершенно такие же бонусные функции, клавиши UI, оформление и персонажи, коэффициенты знаков, диапазон ставки, нестабильность и теоретический процент возврата. Но различие проявляется лишь в том, что неимении рискованности. Покрутить демо вариант слота доступно на портале казино или разработчика, который занимался его разработкой. В гемблинговом клубе сделать это будет проще, потому что в гемблинговом зале казино онлайн находятся гэмблинг-игры разнообразных производителей, а не одного. С применением демо-версий несложно найти несколько игр с большим RTP, уравновешенными другими параметрами и прибыльными бонусными опциями.

В вулкан игровом клубе можно также провести время и на настоящие деньги. Тем не менее эта возможность доступна только тем геймерам, кто достигнул совершеннолетия. Гостю ресурса нужно совершить пару шагов, к примеру, создать учетную запись и пополнить баланс. Эти не потребуют много времени, зато их успешное окончание позволит не только запускать платные версии видеослотов, но и использовать всеми остальными преимуществами: акционными офферами, акциями и ивентами, бетами (если линия присутствует), помощью операторов саппорта при возникновении вопросов или проблем, мобильным приложением, промокодами. Если вдруг потребуется применять дубликат портала для обходного пути блокирования, профиль будет сохранена, следовательно, заново ее создавать заново не понадобится.

Пособие для новичков по записи в казино

Создание аккаунта – начальный шаг, что необходимо выполнить новому игроку в конкретном онлайн-игорном заведении. В большинстве игорных клубов она осуществляется по стандартному сценарию операций, включая и казино онлайн. По указанной причине показанный далее руководство сгодится почти для всякой площадки с азартными играми:

  1. Запустить основной веб-сайт через смартфона и компьютера (можно использовать и приложение, если оно предлагается залом).
  2. Найти иконку «Регистрация» и тыкнуть на нее.
  3. Внести анкетные данные, включая: ФИО, год и дату рождения, адрес электронной почты, контактный телефон, ZIP-код, страну проживания, денежную единицу счета.
  4. Придумать крепкий пароль. Часто игорные заведения просят ввести в учетную запись прописные символы и номера, чтобы усилить уровень безопасности.
  5. Погасить бонусный код для включения приветственного подарка. Это необязательно, так как некоторые заведения выдают презент и без использования бонусного кода.

Часто данного достаточно. Далее платформа отправляет уникальный код на почтовый ящик или номер телефона для подтверждения учетной записи. Заполнив информацию, пользователь перейдет в личный кабинет и будет в состоянии начать процесс верификации, а после этого и к пополнению счета. Большинство казино рекомендуют зарегистрироваться онлайн по упрощенной процедуре, если игрок использует социальными платформами и мессенджинговыми приложениями. К казино вулкан существует шанс быстро зарегистрироваться с помощью VK, OK, Mail.ru, Facebook, Telegram, TikTok, Google.

Уже оказавшись внутри собственном кабинете, игроку рекомендуется пройти верификацию – процесс, в течение которого геймер опознается системой. Вначале понадобится внести данные в персональный учетную запись, если в нем отсутствуют данные, а далее сфотографировать в хорошем разрешении или отсканировать документ, банковской карты, прав на вождение автомобиля. Документ необходимо будет отправить на хостинг и немного ожидать. Как правило верификация требует не более суток в будние дни. Кстати, пользователи могут воспользоваться софтом наподобие CamScanner, которые могут делать первоклассные копии документов без применения специального оборудования.

Защита персональных данных пользователей

Одной из основных задач для каждого онлайн игорного заведения является обеспечение необходимого уровня охраны персональной информации игроков. С этой намерением платформы применяют различные методы и механизмы, а конкретно:

  • 128-разрядное или 256-битное шифрование;
  • кодировочные протоколы
  • анти-мошеннические-системы;
  • антивирусное программное приложение.

Защита денежных операций часто падает на ответственность операторов платежей. Например, ВИЗА и MasterCard применяют 3D Secure, PCI DSS. Зачастую внедряется двойная идентификация, если клиенту необходимо подтвердить действие электронным кодом, SMS-кода на мобильный телефон, или при помощи Face ID/дактилоскопического датчика, если позволяет устройство.

Обладание лицензии у игорного заведения – это существенная гарантия, что на этой площадке следуют норм и законов. Пользователи, зарегистрированные на платформе, могут быть уверены касательно сохранности персональных и финансовой информации. К тому же, игорные организации предоставляют лицензии только после проведения детальных инспекций на предмет открытости условий партнерства, оперативности и честности выплаты выигрышей, функциональности слотов, настольных игр и карточных дисциплин, доступных в каталоге. Прежде чем вовлечься в игру на веб-сайте, следует удостовериться, что игорное заведение на самом деле имеет лицензию. Необходимая сведения регулярно публикуется в подвале домашней страницы, а также на официальной странице лицензиара.

Посетителям онлайн-клубов и собственноручно нужно проявлять внимательность, к примеру, не переходить по разным подозрительным URL-адресам и не заводить аккаунт на игорных платформах, которые не выглядят легальными. Касательно касается денежных операций, то на данный момент наиболее безопасно пользоваться криптовалютой. Транзакции в крипте надежны и конфиденциальны. При таком игрок устранит лишних издержек на значительные сборы, а непосредственно транзакции будут осуществляться весьма оперативно. Разрешено вносить средства с офлайн хранилищ или непосредственно из бирж вроде Binance, Coinbase и прочих.

Как депонировать вклад в казино онлайн

После завершения создания аккаунта и верификации клиенту предстоит совершить заключительный этап к игре на деньги – сделать вклад. Эта самая транзакция как правило происходит в секции «Касса». В казино вулкан казино зайти в данное меню можно как с домашней страницы, так и из аккаунта. После этого игрок обязан будет выбрать вариант оплаты. Не всегда видны все объявленные опции, из-за того, что на это сказывается в немалой степени валюта счета и место жительства игрока. Но не привязанные от местности способы обычно предоставляются каждому игрокам, например, та же самая криптовалюта или цифровые кошельки, Webmoney, Piastrix.

В тот момент, когда посетитель виртуального казино выберет с методом оплаты, необходимо будет предоставить реквизиты карты или электронного, криптовалютного кошелька. Лицензированные азартные сайты не допускают применять учетные данные, принадлежащие посторонним. Кроме того нельзя депонировать на счет в казино средства, добытые нечестным путем, так как работают регламенты по AML. Вследствие несоблюдение данных требований может последовать бессрочная либо временная до выяснения обстоятельств, до выяснения обстоятельств, блокировка учетной записи. В следующем шаге необходимо указать размер перевода, а после этого подтвердить оплату.

Игровые заведения в Сети намереваются гарантировать скорость при выполнении платежных процессов. Это утверждение применимо как к пополнениям, так и к выводам средств. Тем не менее процедура пополнения фондов на аккаунт производится в автоматическом режиме, таким образом длительность ожидания короткое – около 3-5 минут. А здесь запросы на получение анализируются персоналом платформы руками, так что операция может чуть-чуть продлиться, если есть большая очередь. Тем не менее лишь в исключительных ситуациях дожидаться нужно дольше 48 часов.

В любом случае пользователь имеет возможность обратиться в службу поддержки и поинтересоваться прогрессом обработки его вопроса. Важный момент также состоит в том, что забрать из виртуального казино без лишних условий можно исключительно собственный кеш. Что затрагивает дополнительного, то там нередко существуют условия по прокрутке, которые непременно нужно соблюсти. Они постоянно размещаются на странице спецпредложений и детально разъясняются.

Вступительный бонусное предложение от онлайн-казино в интернете

Начинающим игрокам в гэмблинговом заведении начисляется вступительный бонус, который выдается почти каждой гэмблинговым сайтом. В основной массе обстоятельств игроку необходимо зарегистрироваться и внести средства на аккаунт, чтобы активировать награду. Однако имеют возможность добавляться и ещё требования, например, погашение купон-кода в профиле. Распределяются коды партнерскими сайтами системы и прямо администраторами через сотрудничающих с ними стримеров, инфлюенсеров. Подписавшиеся на оповещения электронной почтой и текстовые сообщения тоже периодически получают доступ к акционные коды во время периода акций и ивентов в виртуальном казино с бонусами.

Начальные вознаграждения зачастую предлагаются комплектом, состоящим из безвозмездных спинов и финансов. Большинство гемблинговые площадки возвращают бонус целиком при условии депозита пользователем многих депозитов подряд, что также важно учитывать. Кроме того следует изучить множитель ставки, дабы он не был слишком высоким, – если нет отыграть приз будет трудно. Если положения удовлетворительные, тогда начальный подарок позволит начать играть на виртуальные средства с минимальными рисками для собственных денег. Пользователи останутся оставаться на базовом счете, до тех пор, пока на ставки используются дополнительные средства, а безвозмездные спины в разнообразных слотах позволят вообще не тратить ни свой, ни выданный клубом банкролл.

Некоторыми из онлайн-казино предоставляются бездепы вознаграждения, хотя и намного реже, чем классические приветственные подарки. До их зачислением посетителя могут предложить подписаться на соцсети компании, разместить перепубликацию в чатах, удостоверить электронный ящик или мобильный номер. Время от времени подарками без депозита удивляют постоянных пользователей в преддверии их дня рождения или определённых всеобщих празднеств: Нового Года, Дня всех святых, Пасхи, Рождества. Чтобы вдохновить геймеров загружать портативное или десктопное программное обеспечение, виртуальные казино с акциями тоже предоставляют сувениры, за которые не нужно вносить оплату. Данное естественно достоинство к геймеров, однако имеется и недостаток – высокий процент вейджера, часто сопровождающий бездеп-бонусы. Такое подразумевает, что отыграть бонус будет сложно, особенно при наличии еще и ограничивающих условий по времени. В общем, тут надо внимательно изучать на точное высказывание и делать выбор по ситуации.

Прибыльные события и акции

Если в какой-то момент игроку станет скучно запускать слоты, живые игры, столовые игры и карты с ГСЧ, он сможет принять участие в ивентах. Под ними имеются в виду многообразные прибыльные акции, к примеру, соревнования. Они группируются на интернет и офлайн (специализированные). Первые устраиваются разработчиками программного обеспечения: Drops & Wins (Pragmatic), Spinoleague (Spinomenal). Отличаются чрезвычайно большими выигрышными фондами, вплоть до нескольких миллионов рублей. Последние запускаются самостоятельно игровыми домами, в связи с этим они не предлагают такие значительные выигрыши. Нередко турниры тематические. Игроки вращают в автоматы определенного стиля, отобранные администрацией. Игрок, который получит максимум очков, тот и выиграет приз турнира.

Также очередная категория промоушенов – тиражи. Для того чтобы поучаствовать в них, гостю сайта требуется закупить тикеты. К тому же, особенно если больше пользователь их купит, тем больше будет возможность выиграть капитал. Тем не менее не исключительно наличные разыгрываются актуальными игорными заведениями. В каталоге наград могут находиться мобильные телефоны, таблеты, шикарные машины и дорогие гаджеты. А счастье улыбнется, то есть шанс выиграть и нарастающий куш, или как минимум, его немалую часть. В казино вулкан непрерывно проводят увлекательные акции для учтённых игроков. Оповещаться о данных новостях оперативно обеспечат уведомления (для пользователей приложения), подписка на уведомления электронной почтой и информационный раздел на официальной веб-странице.

Различные виды мероприятия – это дополнительное существенное достоинство интернет-казино по сравнению с наземными залами. Недавние почти не устраивают аналогичных ивентов. Тем не менее в цифровом пространстве гемблинга соревнование значительно выше, так что все площадки стараются привлечь внимание и завлечь игроков. В конечном счете заключительном счете это предоставляет заметные плюсы именно клиентам. К прочим достоинствам виртуальных игорных заведений со слотами можно отнести:

  • крайне стремительные и надежные операции;
  • работа с каким угодно современным платежным системами;
  • безграничный физическим объемом ассортимент игровых машин;
  • многоязычность игрового материала, представленного на сайте;
  • мгновенный доступ к избранным слотам.

Всякий ценитель первоклассных игр на удачу, что соответствует возрастному критерию, прямо сейчас может зайти и зарегистрироваться в казино онлайн казино, чтобы сыграть онлайн в топовые игровые аппараты. Каждые игровые автоматы аттестованы eCOGRA, что подтверждает добросовестности онлайн-казино и его желании предоставить игрокам контент с лицензией и оригинальностью от мировых студий. Сайт создала коллекцию, в где находится около 2000 слотов от известных брендов: Novomatic, Belatra Games, Nolimit City, Hacksaw Gaming, Igrosoft, NetEnt, Spinomenal и прочих разработчиков.

About the Author

Leave a Reply

Your email address will not be published. Required fields are marked *

You may also like these