{# Variables - focus (MauticPlugin\MauticFocusBundle\Entity\Focus) - preview (bool, default: false) - clickUrl (string) #} {%- set style = focus.style -%} {%- set props = focus.properties -%} {%- set useScrollEvent = (props.when in ['scroll_slight', 'scroll_middle', 'scroll_bottom']) -%} {%- set useUnloadEvent = ('leave' == props.when) -%} {%- set useTimeout = props.timeout|default(0) -%} {%- set animate = props.animate is not defined or (props.animate is defined and props.animate is not empty) -%} {%- set linkActivation = props.link_activation is not defined or (props.link_activation is defined and props.link_activation is not empty) -%} {%- set clickUrl = clickUrl|default(props.content.link_url) -%} {%- if '5seconds' == props.when -%} {%- set useTimeout = 5 -%} {%- elseif 'minute' == props.when -%} {%- set useTimeout = 60 -%} {%- endif -%} {%- if useTimeout > 0 -%} {%- set timeout = useTimeout * 1000 -%} {%- endif -%} {%- set cssContent = include('@MauticFocus/Builder/style.less.twig', { 'preview': preview, 'focus': focus, }, with_context=false) -%} {%- set parentCssContent = include('@MauticFocus/Builder/parent.less.twig', { 'preview': preview, }, with_context=false) -%} {%- if 'bar' is same as style -%} {%- set iframeClass = 'mf-bar-iframe mf-bar-iframe-' ~ props.bar.placement ~ ' mf-bar-iframe-' ~ props.bar.size -%} {%- if props.bar.sticky -%} {% set iframeClass = iframeClass ~ ' mf-bar-iframe-sticky' -%} {%- endif -%} {%- elseif 'modal' is same as style or 'notification' is same as style -%} {%- set iframeClass = 'mf-' ~ style ~ '-iframe mf-' ~ style ~ '-iframe-' ~ props[style].placement|replace({'_': '-'}) -%} {%- else -%} {%- set iframeClass = 'mf-' ~ style ~ '-iframe' -%} {%- endif -%} (function (window) { if (typeof window.MauticFocusParentHeadStyleInserted == 'undefined') { window.MauticFocusParentHeadStyleInserted = false; } window.MauticFocus{{ focus.id }} = function () { var Focus = { debug: {{ ('dev' == app.environment) ? 'true' : 'false' }}, modalsDismissed: {}, ignoreConverted: {% if 'notification' is not same as focus.type and props.stop_after_conversion is defined and props.stop_after_conversion is not empty %}true{% else %}false{% endif %}, ignoreClosed: {% if props.stop_after_close is defined and props.stop_after_close is not empty %}true{% else %}false{% endif %}, // Initialize the focus initialize: function () { if (Focus.debug) console.log('initialize()'); Focus.insertStyleIntoHead(); Focus.registerFocusEvent(); // Add class to body Focus.addClass(document.getElementsByTagName('body')[0], 'MauticFocus{{ style|capitalize }}'); }, // Register click events for toggling bar, closing windows, etc registerClickEvents: function () { {% if 'bar' == style %} var isTop = Focus.hasClass(Focus.iframeFocus, 'mf-bar-top'); Focus.setDefaultBarPosition(isTop); var collapser = document.getElementsByClassName('mf-bar-collapser-{{ focus.id }}'); if (collapser[0]) { collapser[0].addEventListener('click', function () { Focus.toggleBarCollapse(collapser[0], false); }); } {% else %} var closer = Focus.iframeDoc.getElementsByClassName('mf-{{ style }}-close'); var aTag = closer[0].getElementsByTagName('a'); var container = Focus.iframeDoc.getElementsByClassName('mf-{{ style }}'); container.onclick = function(e) { if (e) { e.stopPropagation(); } else { window.event.cancelBubble = true; } }; aTag[0].addEventListener('click', function (event) { if (typeof Focus.modalsDismissed["{{ focus.id }}"] == 'undefined') { Focus.incrementCloseCount(); } document.dispatchEvent(new CustomEvent("focus_{{ focus.id }}_close")); }); document.addEventListener("focus_{{ focus.id }}_close", function (event) { // Prevent multiple engagements for link clicks on exit intent Focus.modalsDismissed["{{ focus.id }}"] = true; // Remove iframe if (Focus.iframe.parentNode) { Focus.iframe.parentNode.removeChild(Focus.iframe); } var overlays = document.getElementsByClassName('mf-modal-overlay-{{ focus.id }}'); if (overlays.length) { overlays[0].parentNode.removeChild(overlays[0]); } }); {% endif %} {% if 'link' == focus.type %} var links = Focus.iframeDoc.getElementsByClassName('mf-link'); if (links.length) { links[0].addEventListener('click', function (event) { Focus.convertVisitor(); }); } {% elseif 'form' == focus.type %} var buttons = Focus.iframeDoc.getElementsByClassName('mauticform-button'); if (buttons.length) { buttons[0].addEventListener('click', function (event) { Focus.convertVisitor(); }); } {% endif %} }, setDefaultBarPosition: function (isTop) { if (isTop) { Focus.iframe.style.marginTop = 0; }else { Focus.iframe.style.marginBottom = 0; } }, toggleBarCollapse: function (collapser, useCookie) { var svg = collapser.getElementsByTagName('svg'); var g = svg[0].getElementsByTagName('g'); var currentSize = svg[0].getAttribute('data-transform-size'); var currentDirection = svg[0].getAttribute('data-transform-direction'); var currentScale = svg[0].getAttribute('data-transform-scale'); if (useCookie) { if (Focus.cookies.hasItem('mf-bar-collapser-{{ focus.id }}')) { var newDirection = Focus.cookies.getItem('mf-bar-collapser-{{ focus.id }}'); if (isNaN(newDirection)) { var newDirection = currentDirection; } } else { // Set cookie with current direction var newDirection = currentDirection; } } else { var newDirection = (parseInt(currentDirection) * -1); Focus.cookies.setItem('mf-bar-collapser-{{ focus.id }}', newDirection); } setTimeout(function () { g[0].setAttribute('transform', 'scale(' + currentScale + ') rotate(' + newDirection + ' ' + currentSize + ' ' + currentSize + ')'); svg[0].setAttribute('data-transform-direction', newDirection); }, 500); var isTop = Focus.hasClass(Focus.iframeFocus, 'mf-bar-top'); if ((!isTop && newDirection == 90) || (isTop && newDirection == -90)) { // Open it up Focus.setDefaultBarPosition(isTop); Focus.removeClass(collapser, 'mf-bar-collapsed'); Focus.enableIframeResizer(); } else { // Collapse it var iframeHeight = Focus.iframe.style.height; iframeHeight.replace('px', ''); var newMargin = (parseInt(iframeHeight) * -1) + 'px'; if (isTop) { Focus.iframe.style.marginTop = newMargin; } else { Focus.iframe.style.marginBottom = newMargin; } Focus.addClass(collapser, 'mf-bar-collapsed'); Focus.disableIFrameResizer(); } }, // Register scroll events, etc registerFocusEvent: function () { window.addEventListener('resize', function () { Focus.disableIFrameResizer(); Focus.enableIframeResizer(); }); if (Focus.debug) console.log('registerFocusEvent()'); {% if useScrollEvent %} if (Focus.debug) console.log('scroll event registered'); {% if useTimeout %} if (Focus.debug) console.log('timeout event registered'); setTimeout(function () { window.addEventListener('scroll', Focus.engageVisitorAtScrollPosition); }, {{ timeout }}); {% else %} window.addEventListener('scroll', Focus.engageVisitorAtScrollPosition); {% endif %} {% elseif useUnloadEvent %} if (Focus.debug) console.log('show when visitor leaves'); {% if useTimeout %} if (Focus.debug) console.log('timeout event registered'); setTimeout(function () { document.documentElement.addEventListener('mouseleave', Focus.engageVisitor); }, {{ timeout }}); {% else %} document.documentElement.addEventListener('mouseleave', Focus.engageVisitor); {% endif %} // Add a listener to every link {% if linkActivation %} var elements = document.getElementsByTagName('a'); for (var i = 0, len = elements.length; i < len; i++) { var href = elements[i].getAttribute('href'); if (href && href.indexOf('#') != 0 && href.indexOf('javascript:') != 0) { elements[i].onclick = function (event) { if (typeof Focus.modalsDismissed["{{ focus.id }}"] == 'undefined') { if (Focus.engageVisitor()) { event.preventDefault(); } } } } } {% endif %} {% else %} if (Focus.debug) console.log('show immediately'); {% if useTimeout %} if (Focus.debug) console.log('timeout event registered'); setTimeout(function () { // Give a slight delay to allow browser to process style injection into header Focus.engageVisitor(); }, {{ timeout }}); {% else %} // Give a slight delay to allow browser to process style injection into header Focus.engageVisitor(); {% endif %} {% endif %} }, // Insert global style into page head insertStyleIntoHead: function () { if (!window.MauticFocusParentHeadStyleInserted) { if (Focus.debug) console.log('insertStyleIntoHead()'); var css = "{{- parentCssContent|e('js') -}}", head = document.head || document.getElementsByTagName('head')[0], style = document.createElement('style'); head.appendChild(style); style.type = 'text/css'; if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } else if (Focus.debug) { console.log('Shared style already inserted into head'); } }, // Inserts styling into the iframe's head insertFocusStyleIntoIframeHead: function () { // Insert style into iframe header var frameDoc = Focus.iframe.contentDocument; var frameHead = frameDoc.getElementsByTagName('head').item(0); var css = "{{- cssContent|e('js') -}}"; var style = frameDoc.createElement('style'); style.type = 'text/css'; if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(frameDoc.createTextNode(css)); } frameHead.appendChild(style); var metaTag = frameDoc.createElement('meta'); metaTag.name = "viewport" metaTag.content = "width=device-width,initial-scale=1,minimum-scale=1.0 maximum-scale=1.0" frameHead.appendChild(metaTag); }, // Generates the focus HTML engageVisitor: function () { var now = Math.floor(Date.now() / 1000); if (Focus.cookies.hasItem('mautic_focus_{{ focus.id }}')) { if (Focus.debug) console.log('Cookie exists thus checking frequency'); var lastEngaged = parseInt(Focus.cookies.getItem('mautic_focus_{{ focus.id }}')), frequency = '{{ props.frequency }}', engage; if (Focus.ignoreConverted && lastEngaged == -1) { if (Focus.debug) console.log('Visitor converted; abort'); return false; } switch (frequency) { case 'once': engage = false; if (Focus.debug) console.log('Engage once, abort'); break; case 'everypage': engage = true; if (Focus.debug) console.log('Engage on every page, continue'); break; case 'q2min': engage = (now - lastEngaged) >= 120; if (Focus.debug) { var debugMsg = 'Engage q2 minute, '; if (engage) { debugMsg += 'continue'; } else { debugMsg += 'engage in ' + (120 - (now - lastEngaged)) + ' seconds'; } console.log(debugMsg); } break; case 'q15min': engage = (now - lastEngaged) >= 900; if (Focus.debug) { var debugMsg = 'Engage q15 minute, '; if (engage) { debugMsg += 'continue'; } else { debugMsg += 'engage in ' + (120 - (now - lastEngaged)) + ' seconds'; } console.log(debugMsg); } break; case 'hourly': engage = (now - lastEngaged) >= 3600; if (Focus.debug) { var debugMsg = 'Engage hourly, '; if (engage) { debugMsg += 'continue'; } else { debugMsg += 'engage in ' + (120 - (now - lastEngaged)) + ' seconds'; } console.log(debugMsg); } break; case 'daily': engage = (now - lastEngaged) >= 86400; if (Focus.debug) { var debugMsg = 'Engage daily, '; if (engage) { debugMsg += 'continue'; } else { debugMsg += 'engage in ' + (120 - (now - lastEngaged)) + ' seconds'; } console.log(debugMsg); } break; } if (!engage) { return false; } } if (Focus.ignoreClosed && Focus.getCloseCount() > 0) { if (Focus.debug) console.log('Visitor has closed the focus; abort'); return false; } if (Focus.debug) console.log('engageVisitor()'); // Inject iframe Focus.createIframe(); // Inject content into iframe Focus.iframeDoc.open(); Focus.iframeDoc.write("{focus_content}"); Focus.iframeDoc.close(); var animate = {% if animate %}true{% else %}false{% endif %}; Focus.iframe.onload = function() { if (Focus.debug) console.log('iframe loaded for '+Focus.iframe.getAttribute('src')); // Resize iframe if (Focus.enableIframeResizer()) { // Give iframe chance to resize setTimeout(function () { if (animate) { Focus.addClass(Focus.iframe, "mf-animate"); } Focus.addClass(Focus.iframe, "mf-loaded"); }, 35); } else { if (animate) { Focus.addClass(Focus.iframe, "mf-animate"); } Focus.addClass(Focus.iframe, "mf-loaded"); } } // Set body margin to 0 Focus.iframeDoc.getElementsByTagName('body')[0].style.margin = 0; Focus.iframeDoc.getElementsByTagName('body')[0].style.overflowX = 'hidden'; // Find elements that should be moved to parent var move = Focus.iframeDoc.getElementsByClassName('mf-move-to-parent'); for (var i = 0; i < move.length; i++) { var bodyFirstChild = document.body.firstChild; Focus.addClass(move[i], 'mf-moved-{{ focus.id }}'); bodyFirstChild.parentNode.insertBefore(move[i], Focus.iframe); } // Find elements that should be copied to parent var copy = Focus.iframeDoc.getElementsByClassName('mf-copy-to-parent'); for (var i = 0; i < copy.length; i++) { var bodyFirstChild = document.body.firstChild; var clone = copy[i].cloneNode(true); Focus.addClass(clone, 'mf-moved-{{ focus.id }}'); bodyFirstChild.parentNode.insertBefore(clone, Focus.iframe); } // Get the main focus element var focus = Focus.iframeDoc.getElementsByClassName('mautic-focus'); Focus.iframeFocus = focus[0]; // Insert style into iframe head Focus.insertFocusStyleIntoIframeHead(); // Register events Focus.registerClickEvents(); {% if 'leave' == props.when %} // Ensure user can leave document.documentElement.removeEventListener('mouseleave', Focus.engageVisitor); {% endif %} // Add cookie of last engagement if (Focus.debug) console.log('mautic_focus_{{ focus.id }} cookie set for ' + now); Focus.cookies.removeItem('mautic_focus_{{ focus.id }}'); Focus.cookies.setItem('mautic_focus_{{ focus.id }}', now, Infinity, '/'); {% if 'bar' == style %} var collapser = document.getElementsByClassName('mf-bar-collapser-{{ focus.id }}'); if (animate) { // Give iframe chance to resize setTimeout(function () { Focus.toggleBarCollapse(collapser[0], true); }, 35); } else { Focus.toggleBarCollapse(collapser[0], true); } {% endif %} return true; }, // Enable iframe resizer enableIframeResizer: function () { if (typeof Focus.iframeResizerEnabled !== 'undefined') { return true; } {% if style in ['modal', 'notification', 'bar'] %} Focus.iframeHeight = 0; Focus.iframeWidth = 0; Focus.iframeResizeInterval = setInterval(function () { if (Focus.iframeHeight !== Focus.iframe.style.height) { var useHeight = ((window.innerHeight < Focus.iframeFocus.offsetHeight) ? window.innerHeight : Focus.iframeFocus.offsetHeight); useHeight += 10; useHeight = useHeight + 'px'; if (Focus.debug) { console.log('window inner height = ' + window.innerHeight); console.log('iframe offset height = ' + Focus.iframeFocus.offsetHeight); console.log('iframe height set to ' + useHeight) } Focus.iframe.style.height = useHeight; Focus.iframeHeight = useHeight; } {% if style in ['modal', 'notification'] %} if (Focus.iframeWidth !== Focus.iframe.style.width) { if (Focus.debug) { console.log('window inner width = ' + window.innerWidth); console.log('iframe offset width = ' + Focus.iframeFocus.offsetWidth); } if (window.innerWidth < Focus.iframeFocus.offsetWidth) { // Responsive iframe Focus.addClass(Focus.iframeFocus, 'mf-responsive'); Focus.addClass(Focus.iframe, 'mf-responsive'); Focus.iframe.style.width = window.innerWidth + 'px'; Focus.iframe.width = window.innerWidth; if (Focus.debug) console.log('iframe set to responsive width: '); } else { Focus.iframe.style.width = Focus.iframeFocus.offsetWidth + 'px'; Focus.iframe.width = Focus.iframeFocus.offsetWidth + 'px'; Focus.removeClass(Focus.iframeFocus, 'mf-responsive'); Focus.removeClass(Focus.iframe, 'mf-responsive'); if (Focus.debug) console.log('iframe not a responsive width'); } Focus.iframeWidth = Focus.iframe.style.width; } {% endif %} }, 35); Focus.iframeResizerEnabled = true; return true; {% else %} return false; {% endif %} }, // Disable iframe resizer disableIFrameResizer: function () { if (typeof Focus.iframeResizerEnabled !== 'undefined') { delete(Focus.iframeResizerEnabled); } {% if style in ['modal', 'notification', 'bar'] %} clearInterval(Focus.iframeResizeInterval); {% endif %} }, // Create iframe to load into body createIframe: function () { if (Focus.debug) console.log('createIframe()'); Focus.iframe = document.createElement('iframe'); Focus.iframe.style.border = 0; Focus.iframe.style.width = "100%"; Focus.iframe.style.height = "100%"; Focus.iframe.src = "about:blank"; Focus.iframe.scrolling = "auto"; Focus.iframe.className = "{{ iframeClass|raw }}"; var bodyFirstChild = document.body.firstChild; bodyFirstChild.parentNode.insertBefore(Focus.iframe, bodyFirstChild); Focus.iframeDoc = Focus.iframe.contentWindow.document; }, // Execute event at current position engageVisitorAtScrollPosition: function (event) { var visualHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight; var scrollPos = window.pageYOffset, atPos = 0; {% if 'scroll_slight' is same as props.when %} atPos = 10; {% elseif 'scroll_middle' is same as props.when %} scrollPos += (visualHeight / 2); atPos = (document.body.scrollHeight / 2); {% elseif 'scroll_bottom' is same as props.when %} scrollPos += visualHeight; atPos = document.body.scrollHeight; {% endif %} if (Focus.debug) console.log('scrolling: ' + scrollPos + ' >= ' + atPos); if (scrollPos >= atPos) { window.removeEventListener('scroll', Focus.engageVisitorAtScrollPosition); Focus.engageVisitor(); } }, // Create cookie noting visitor has been converted if applicable convertVisitor: function () { if (Focus.ignoreConverted) { if (Focus.debug) console.log('Visitor converted'); Focus.cookies.setItem('mautic_focus_{{ focus.id }}', -1, Infinity, '/'); } else if (Focus.debug) { console.log('Visitor converted but ignoreConverted not enabled'); } }, // Element has class hasClass: function (element, hasClass) { return ( (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(" " + hasClass + " ") > -1 ); }, // Add class to element addClass: function (element, addClass) { if (!Focus.hasClass(element, addClass)) { element.className += " " + addClass; } }, // Remove class from element removeClass: function (element, removeClass) { element.className = element.className.replace(new RegExp('\\b' + removeClass + '\\b'), ''); }, getCloseCount() { if (Focus.cookies.hasItem('mautic_focus_{{ focus.id }}_closed')) { return parseInt(Focus.cookies.getItem('mautic_focus_{{ focus.id }}_closed')) } else { return 0; } }, incrementCloseCount() { var closeCount = Focus.getCloseCount(); Focus.cookies.setItem('mautic_focus_{{ focus.id }}_closed', ++closeCount); }, // Cookie handling cookies: { /** * :: cookies.js :: * https://developer.mozilla.org/en-US/docs/Web/API/document.cookie * http://www.gnu.org/licenses/gpl-3.0-standalone.html */ getItem: function (sKey) { if (!sKey) { return null; } return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; }, setItem: function (sKey, sValue, vEnd, sPath, sDomain) { if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } this.removeItem(sKey); var sExpires = ""; if (vEnd) { switch (vEnd.constructor) { case Number: sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; break; case String: sExpires = "; expires=" + vEnd; break; case Date: sExpires = "; expires=" + vEnd.toUTCString(); break; } } document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + "; secure"; return true; }, removeItem: function (sKey, sPath, sDomain) { if (!this.hasItem(sKey)) { return false; } document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : ""); return true; }, hasItem: function (sKey) { if (!sKey) { return false; } return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); }, keys: function () { var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/); for (var nLen = aKeys.length, nIdx = 0; nIdx < nLen; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); } return aKeys; } } }; return Focus; } // Initialize MauticFocus{{ focus.id }}().initialize(); })(window);