/*! waitForImages jQuery Plugin 2016-01-04 */
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){var b="waitForImages";a.waitForImages={hasImageProperties:["backgroundImage","listStyleImage","borderImage","borderCornerImage","cursor"],hasImageAttributes:["srcset"]},a.expr[":"]["has-src"]=function(b){return a(b).is('img[src][src!=""]')},a.expr[":"].uncached=function(b){return a(b).is(":has-src")?!b.complete:!1},a.fn.waitForImages=function(){var c,d,e,f=0,g=0,h=a.Deferred();if(a.isPlainObject(arguments[0])?(e=arguments[0].waitForAll,d=arguments[0].each,c=arguments[0].finished):1===arguments.length&&"boolean"===a.type(arguments[0])?e=arguments[0]:(c=arguments[0],d=arguments[1],e=arguments[2]),c=c||a.noop,d=d||a.noop,e=!!e,!a.isFunction(c)||!a.isFunction(d))throw new TypeError("An invalid callback was supplied.");return this.each(function(){var i=a(this),j=[],k=a.waitForImages.hasImageProperties||[],l=a.waitForImages.hasImageAttributes||[],m=/url\(\s*(['"]?)(.*?)\1\s*\)/g;e?i.find("*").addBack().each(function(){var b=a(this);b.is("img:has-src")&&!b.is("[srcset]")&&j.push({src:b.attr("src"),element:b[0]}),a.each(k,function(a,c){var d,e=b.css(c);if(!e)return!0;for(;d=m.exec(e);)j.push({src:d[2],element:b[0]})}),a.each(l,function(a,c){var d=b.attr(c);return d?void j.push({src:b.attr("src"),srcset:b.attr("srcset"),element:b[0]}):!0})}):i.find("img:has-src").each(function(){j.push({src:this.src,element:this})}),f=j.length,g=0,0===f&&(c.call(i[0]),h.resolveWith(i[0])),a.each(j,function(e,j){var k=new Image,l="load."+b+" error."+b;a(k).one(l,function m(b){var e=[g,f,"load"==b.type];return g++,d.apply(j.element,e),h.notifyWith(j.element,e),a(this).off(l,m),g==f?(c.call(i[0]),h.resolveWith(i[0]),!1):void 0}),j.srcset&&(k.srcset=j.srcset),k.src=j.src})}),h.promise()}});
window.addEventListener('message', receiveMessage, false);

var stickyHeaderMargin = 0;

function receiveMessage(evt)
{
	let eventClosePopup = true;

	if (evt.origin != siteURL && evt.origin != applicationSiteURL)
		return false;

	if (typeof evt.data.action === "undefined")
		return false;

	switch (evt.data.action)
	{
		case 'filebrowserFile':
			$('#' + filebrowserField).val(evt.data.url);
			$('#' + filebrowserField).trigger('change');
			closePopup('filebrowserPopup');
			break;

		case 'insertBloc':
		case 'restoreBloc':
		case 'copyBloc':

			if (evt.data.reload)
			{
				toggleLoader();
				window.location.reload();
			}
			else
			{
				$('.cms-section[data-sectionID="'+ evt.data.sectionID +'"] *[data-column="'+ evt.data.column +'"] .section-content .bloc-list').append(evt.data.contents);
				$('*[data-blocID="'+ evt.data.blocID +'"]').hide(0).fadeIn(1500, function() {
					lazyload();
					refreshParallax();
				});
				updateTrashCount(evt.data.trashBlocs);
				activateEffectsOnBlocks();
				activateDisabledLinks();
				scrollToAnchor('div[data-blocid="'+ evt.data.blocID +'"]', 750, 200);
			}

			break;

		case 'updateBloc':

			if (evt.data.reload)
			{
				toggleLoader();
				showReloadMessage(evt.data.message);
			}
			else
			{
				var bloc = $('*[data-blocID="'+ evt.data.blocID +'"]');
				var section = bloc.parents('.cms-section[data-sectionID]');

				bloc.fadeOut('slow', function() {
					// Change bloc section
					if (evt.data.section != section.attr('data-section'))
						bloc.appendTo($('.cms-section[data-section="'+ evt.data.section +'"] .section-content .bloc-list'));

					bloc.replaceWith(evt.data.contents).fadeIn('slow', function() {
						lazyload();
						activateEffectsOnBlocks();
						activateDisabledLinks();
						refreshParallax();
						scrollToAnchor('div[data-blocid="'+ evt.data.blocID +'"]', 750, 200);
					});
				});
			}

			break;

		case 'updateBlocPage':
			var bloc = $('*[data-blocID="'+ evt.data.blocID +'"]');

			bloc.attr('data-isEditable', false);

			bloc.hide('slow', function() {
				bloc.remove();
			});

			Swal.fire({
				position: 'center',
			  type: 'info',
			  text: evt.data.message,
				backdrop: false,
				showCancelButton: true,
				cancelButtonText: 'Fermer',
				confirmButtonText: 'Voir la page',
				backdrop: true,
				customClass: 'custom-swal-popup'
			}).then((result) => {
				if (result.value === true)
					window.location.href = evt.data.pageURL;
				else
					window.location.reload();
			});
			break;

		case 'deleteBloc':
			var bloc = $('*[data-blocID="'+ evt.data.blocID +'"]');

			bloc.attr('data-isEditable', false);

			bloc.hide('slow', function() {
				updateTrashCount(evt.data.trashBlocs);
				bloc.remove();
				refreshParallax();
			});
			break;

		case 'deleteTrashBloc':
			eventClosePopup = false;
			updateTrashCount(evt.data.trashBlocs);
			showMessage(evt.data.message);
			break;

		case 'deleteTrashBlocs':
		case 'restoreBlocs':
			updateTrashCount(evt.data.trashBlocs);
			showReloadMessage(evt.data.message);
			break;

		case 'insertPageSection':

			if (evt.data.position == 'top')
				$(evt.data.contents).insertAfter($('.section-new').first());
			else
				$(evt.data.contents).insertBefore($('.section-new').last());


			//window.location.reload();
			$('.cms-section[data-sectionID="'+ evt.data.sectionID +'"]').hide(0).fadeIn(1500, function() {
				lazyload();
				activateEffectsOnBlocks();
				activateDisabledLinks();
				refreshParallax();
				scrollToAnchor('.cms-section[data-sectionID="'+ evt.data.sectionID +'"]', 750, 200);
			});
			break;
			
		case 'updatePageSection':
		case 'updatePageSectionColumn':
		case 'updatePageSectionPage':
			toggleLoader();
			window.location.reload();
			break;

		case 'copyPageSection':
			toggleLoader();

			const url = new URL(window.location.href);
			url.hash = 'page-section-' + evt.data.sectionID;
			url.searchParams.set('rand', getRandString(16));
			window.location.href = url.toString();
			break;

		case 'deleteSection':
			$('*[data-sectionID="'+ evt.data.sectionID +'"]').hide('slow', function() {
				updateTrashCount(evt.data.trashBlocs);
				$('*[data-sectionID="'+ evt.data.sectionID +'"]').remove();
				refreshParallax();
			});
			break;

		case 'insertPage':
			toggleLoader();

			Swal.fire({
				position: 'center',
			  type: 'info',
			  text: evt.data.message,
				backdrop: false,
				showCancelButton: true,
				cancelButtonText: 'Fermer',
				confirmButtonText: 'Voir la page',
				backdrop: true,
				customClass: 'custom-swal-popup'
			}).then((result) => {
				if (result.value === true)
					window.location.href = evt.data.pageURL;
				else
					window.location.reload();
			});
			break;

		case 'updatePage':
			if (evt.data.pageID == pID)
			{
				toggleLoader();
				showReloadMessage(evt.data.message);
			}
			break;

		case 'importPageContent':
			toggleLoader();
			showReloadMessage(evt.data.message);
			break;

		case 'insertTheme':
		case 'updateTheme':
		case 'setWebsiteTheme':
		case 'updateThemeTemplate':
		case 'customizeTheme':
		case 'customizeThemeAdvanced':
    case 'customizeThemeBanner':
		case 'updateThemeCSS':
			toggleLoader();
			showReloadMessage(evt.data.message);
			break;

		default:
			if (evt.data.message) {
				showMessage(evt.data.message, evt.data.reload);
			} else if (evt.data.reload) {
				window.location.reload();
			}

			activateDisabledLinks();
	}

	if (eventClosePopup)
		closePopup();
} // receiveMessage()

$(document).bind('drop dragover', function (e) {
  e.preventDefault();
});

$(window).on('load', function() {
  lazyLoading();
});

$(window).scroll(function() {
  if ($(window).scrollTop() > 300)
    $('#backToTop').addClass('visible');
  else
    $('#backToTop').removeClass('visible');
});

$(window).resize(function() {
  resizeItems();
	sectionsStretching();
});

$(function() {
	sectionsStretching();
	activateDisabledLinks();
  bannerSlider();
  initParallaxBackground();
	activateEffectsOnBlocks();
	filterOnLightboxPictures();
	updateStickyHeaderMargin($('#frontEdit').outerHeight());

	// System notifications (success)
	if ($('.system-message-success').length)
	{
		displaySweetAlert('success', $('.system-message-success').html(), false, {
			position: 'center',
			backdrop: true
		});
	}

	// System notifications (error)
	if ($('.system-message-error').length)
	{
		displaySweetAlert('error', $('.system-message-error').html(), false, {
			position: 'center',
			backdrop: true
		});
	}

	$(document).on('click', '.close-popup', function(e) {
		e.preventDefault();
		window.parent.closePopup();
	});

	// File browser
	$(document).on('click', '.showFilesBrowser', function(e) {
		e.preventDefault();
		filebrowserField = getURLParameter('returnField', $(this).attr('href'))
		openAdminPopup($(this).attr('title'), $(this).attr('href'), 'filebrowserPopup');
	});

	// Responsive menu
	if ($(document).width() <= 1024)
	{
		// Display/Hide responsive menu
		$('#menu_main .menu-button').click(function() {
			$('#menu_main_responsive').addClass('open');
		});

		$('#menu_main_responsive .menu-button').click(function() {
			$('#menu_main_responsive').removeClass('open');
		});

		$('.website-menu li.dropMenu .arrow').click(function() {
			$(this).parent().children('ul').slideToggle();
			$(this).toggleClass('active');
		});
	}
	else
	{
		// Check if menu is off screen.
		$('.menu_main .dropMenu').on('mouseenter', function() {
			$(this).children('ul').removeClass('left right');

			var parent = $(this).parents('ul');
			var parentOffset = parent.offset();
			var parentPositions = [parentOffset.left, (parentOffset.left + parent.outerWidth())];
			var level = parent.length;
			var className = null;

			if (level == 1)
			{
				var menu = $('.menu_main').offset();
				var menuPositions = [menu.left, (menu.left + $('.menu_main').outerWidth())];

				var item = $(this).children('ul');
				var itemOffset = item.offset();
				var itemPositions = [itemOffset.left, (itemOffset.left + item.outerWidth())];

				if (itemPositions[0] < menuPositions[0])
					className = 'left';
				else if (itemPositions[1] > menuPositions[1])
					className = 'right';
			}
			else
			{
				var positionRight = parentPositions[1] + $(this).outerWidth();

				if (positionRight > $(document).width())
					className = 'left';
				else
					className = 'right';
			}

			if (className)
				$(this).children('ul').addClass(className);
		});
	}

	// Field colorPicker
	$('.resetColor').click(function(e) {
		e.preventDefault();
		var field = $(this).parents('.input-group').find('input');
		field.val('');
		field.removeAttr('style');
	});

	// Password field
	$(document).on('click', '.toggle-password-field', function(e) {
		e.preventDefault();

		var input = $(this).parents('.input-group').find('input');

		if ($(this).hasClass('password-hidden'))
		{
			input.attr('type', 'text');

			$(this).addClass('password-visible');
			$(this).removeClass('password-hidden');
		}
		else
		{
			input.attr('type', 'password');

			$(this).addClass('password-hidden');
			$(this).removeClass('password-visible');
		}
	});

	$(document).on('change', '.filebrowser-with-preview input', function() {
		var url = $(this).val();
		var preview = $(this).parents('.filebrowser-with-preview').find('.picture-field-preview a');

		// Remove current preview
		preview.html('');

		if (url)
		{
			var extension = url.split('.').pop();
			var filename = url.split('/').pop();
			var pictureExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif'];
			var isPicture = pictureExtensions.includes(extension);

			if (isPicture)
			{
				// Display picture preview
				$('<img src="'+ encodeURI(url) +'" />').appendTo(preview);
			}
			else
			{
				// Display filename (for video or other files)
				$('<span class="unsupported-fallback">'+ filename +'</span>').appendTo(preview);
			}
			
			$(this).parents('.filebrowser-with-preview').find('.picture-field-preview').addClass('active');
		}
		else
		{
			$(this).parents('.filebrowser-with-preview').find('.picture-field-preview').removeClass('active');
		}
	});

	$(document).on({
    mouseenter: function () {
			if ($(this).find('img').length)
			{
				$(this).find('.hover').animate({
					opacity: 1
				}, 300);
			}
    },
    mouseleave: function () {
			$(this).find('.hover').animate({
				opacity: 0
			}, 300);
    }
	}, '.picture-field-preview');

	// File Upload
	$(document).on('click', '.filebrowser-with-preview .remove-picture', function(e) {
		e.preventDefault();
		$(this).parents('.filebrowser-with-preview').find('input').val('');
		$(this).parents('.filebrowser-with-preview').find('img').remove();
		$(this).parents('.filebrowser-with-preview').find('.picture-field-preview').removeClass('active');
	});

	$(document).on('click', '.filebrowser-with-preview .use-external-picture', function(e) {
		e.preventDefault();

		var input = $(this).parents('.filebrowser-with-preview').find('input');

		Swal.fire({
			position: 'center',
			type: 'info',
			text: 'Entrez l\'adresse de la photo que vous souhaitez utiliser.',
			input: 'text',
			showCancelButton: true,
			cancelButtonText: i18n.general.close,
			confirmButtonText: i18n.general.validate,
			backdrop: true,
			customClass: 'custom-swal-popup'
		}).then((result) => {
			if (result.value)
			{
				input.val(result.value);
				input.trigger('input');
			}
		});
	});

	$(document).on('click', '.form-file-upload .preview-container .preview', function(e) {
		if (e.target !== this) return;

		$(this).parents('.form-file-upload').find('.button').click();
	});

	$(document).on('click', '.form-file-upload .button', function(e) {
		e.preventDefault();

		var uploadZone = $(this).parents('.form-file-upload');
		var inputFile = uploadZone.find('input[type="file"]');

		inputFile.prop('disabled', false);
		inputFile.click();
		inputFile.prop('disabled', true);
	});

	$(document).on('change', '.form-file-upload input[type="file"]', function(e) {
		var uploadZone = $(this).parents('.form-file-upload');
		var files = $(this).prop("files");
		var inputName = uploadZone.find('input[type="file"]').attr('name');

		uploadZone.find('.preview').fadeIn('slow');

		if (files.length)
			for (var i = 0 ; i < files.length ; i++)
				previewUploadedFile(files[i], inputName, uploadZone);
	});

	$(document).on('dragover', '.form-file-upload', function(e) {
    e.preventDefault();
    e.stopPropagation();
    $(this).addClass('active');
	});

	$(document).on('dragleave', '.form-file-upload', function(e) {
    e.preventDefault();
    e.stopPropagation();
    $(this).removeClass('active');
	});

	$(document).on('drop', '.form-file-upload', function(e) {
    if (e.originalEvent.dataTransfer)
		{
    	if (e.originalEvent.dataTransfer.files.length)
			{
        e.preventDefault();
        e.stopPropagation();

				var uploadZone = $(this);
				var inputName = uploadZone.find('input[type="file"]').attr('name');

				uploadZone.find('.preview').fadeIn('slow');

				for (var i = 0 ; i < e.originalEvent.dataTransfer.files.length ; i++)
					previewUploadedFile(e.originalEvent.dataTransfer.files[i], inputName, uploadZone);
    	}
    }

    $(this).removeClass('active');
    return false;
	});

	$(document).on('click', '.form-file-upload .picture .delete', function(e) {
		var formFileUpload = $(this).parents('.form-file-upload');

		$(this).parents('.picture').fadeOut('slow', function() {
			$(this).remove();

			//if (!formFileUpload.find('.picture').length)
				//formFileUpload.find('.preview-container').hide();
				//formFileUpload.find('.preview').fadeOut('slow');

			var countFilesItem = formFileUpload.find('.files-count span');
			var countFiles = parseInt(countFilesItem.text());
			var maximumFiles = formFileUpload.attr('data-maximum-files') ? parseInt(formFileUpload.attr('data-maximum-files')) : false;

			countFilesItem.text(countFiles - 1);
		});
	});

	$('#fileBrowserUploadForm').submit(function(e) {
		$(this).find('input[type="submit"]').prop('disabled', true);
		$('.upload-waiting').fadeIn('fast');
	});

	// Resize item
	var isIE = /*@cc_on!@*/false || !!document.documentMode; // Internet Explorer 6-11
	var isEdge = !isIE && !!window.StyleMedia; // Edge 20+

	if (isIE || isEdge)
	{
		$('.resizeItem').children('img, iframe, video').addClass('resizableCompatibility');

		$('.resizeItem img').waitForImages({
			each: function() {
				resizeItem($(this).parents('.resizeItem'));
			},
			waitForAll: true
		});

		$('.resizeItem[data-ratio]').each(function() {
			resizeItem($(this));
		});
	}

	$('.ratioItem > img').on('load', function(event) {
	  checkRatioItem($(this).parent(), $(this));
	});

	// Site offline page
	$(document).on('click', '.site-offline-login', function(e) {
		e.preventDefault();
		$(this).hide();
		$('#formLogin').slideDown();
	});

	/**
	 * Admin login page
	 */

	$(document).on('click', '.toggle-password-lost-form', function(e) {
		e.preventDefault();

		if ($('#formPasswordResetBox').is(':visible'))
		{
			$('#formPasswordResetBox').fadeOut('slow', function() {
				$('.login-box').fadeIn('slow');
			});
		}
		else
		{
			if ($('#formPasswordLostBox').is(':visible')) {
				$('#formPasswordLostBox').fadeOut('slow', function() {
					$('.login-box').fadeIn('slow');
				});
			} else {
				$('.login-box').fadeOut('slow', function() {
					$('#formPasswordLostBox').fadeIn('slow');
				});
			}
		}
	});

	$(document).on('click', '.toggle-password-reset-form', function(e) {
		e.preventDefault();
		$('#formPasswordResetBox').fadeOut('slow', function() {
			$('.login-box').fadeIn('slow');
		});
	});

	$(document).on('click', '.share-link-field', function() {
		$(this).select();
		copyToClipboard($(this));
	});

	$('#backToTop').on('click', function(e) {
	  e.preventDefault();
	  $('html, body').animate({
			scrollTop: 0
		}, '300');
	});

	// Spoiler
	$(document).on('click', '.spoiler .spoiler-toggle', function() {
		$(this).hide();
		$(this).next().show();
	});

	// Smooth scrolling on anchors
	$(document).on('click', '.anchor-smooth-scroll', function(e) {
		var url = $(this).attr('href');

		if (url.slice(0,1) == '#')
		{
			scrollToAnchor(url);
			return false;
		}
	});

	/* Translate content */
  $(document).on('click', '.get-automatic-translation', function() {
    var csrf = $(this).attr('data-csrf');
    var sourceContent = $(this).attr('data-source-content');
    var sourceType = $(this).attr('data-source-type');
    var sourceID = $(this).attr('data-source-id');
    var cultureList = getAvailableCultures();
		var fieldsHTML = '';

		fieldsHTML += '<div class="col-12 text-left">';
			fieldsHTML += '<label for="sourceCulture">Source de la traduction</label>';
			fieldsHTML += '<select name="sourceCulture" id="sourceCulture" class="swal2-input">';

				for (var key in cultureList)
					fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

			fieldsHTML += '</select>';
		fieldsHTML += '</div>';
		
		fieldsHTML += '<div class="col-12 text-left">';
			fieldsHTML += '<label><input type="checkbox" name="contentReplace" id="contentReplace"> Remplacer les textes existants</label>';
		fieldsHTML += '</div>';

    Swal.fire({
      title: 'Traduction automatique',
			html: '<div class="row">' + fieldsHTML + '</div>',
      showCancelButton: true,
      confirmButtonText: i18n.general.validate,
      confirmButtonColor: '#4caf50',
      cancelButtonText: i18n.general.cancel,
      cancelButtonColor: '#f44336',
			preConfirm: function () {
				return new Promise(function (resolve) {
					resolve({
            sourceCulture: $('#sourceCulture').val(),
            contentReplace: $('#contentReplace').is(':checked') ? 1 : 0
          })
				});
			}
    }).then((result) => {
      if (result.value)
      {
        toggleLoader();
				
				var sourceCulture = result.value.sourceCulture;
				var contentReplace = result.value.contentReplace;

        if (sourceContent == 'form')
        {
          // Translate from form fields data
          $.getJSON(ajaxURL + 'cms_getTranslationFields', {
            sourceType: sourceType,
            sourceID: sourceID,
            sourceCulture: sourceCulture,
            csrf_translateContent: csrf
          }, function(json) {
            if (json.error)
            {
              showError(json.data.message);
              toggleLoader();
            }
            else
            {
							var fieldList = json.data.fields;
							
							for (var culture in json.data.fields)
							{
								for (let j = 0; j < json.data.fields[culture].length; j++)
								{
									let field = json.data.fields[culture][j];

									if (field['type'] == 'ckeditor')
										fieldList[culture][j]['value'] = CKEDITOR.instances[field['id']].getData();
									else
										fieldList[culture][j]['value'] = $('#' + field['id']).val();
								}
							}

              translateContent(sourceContent, sourceType, sourceID, sourceCulture, csrf, fieldList, contentReplace);
            }
					});
        }
        else if (sourceContent == 'origin')
        {
          // Translate from current element content (from database)
          translateContent(sourceContent, sourceType, sourceID, sourceCulture, csrf, null, contentReplace);
        }
      }
    });
  });

	$(document).on('click', '.generate-content', function(e) {
		e.preventDefault();
		
		var mode = $(this).attr('data-mode');
		var targetField = $(this).attr('data-target-field');
		var csrf = $(this).attr('data-csrf');
		var cultureList = getAvailableCultures();
		var fieldsHTML = '';

		switch (mode)
		{
			case 'custom':

				var title = 'Demande personnalisée';
			
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentSubject">Votre demande</label>';
					fieldsHTML += '<textarea name="contentSubject" id="contentSubject" placeholder="Détaillez votre demande" class="swal2-textarea"></textarea>';
				fieldsHTML += '</div>';

				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentCulture">Langue de rédaction</label>';
					fieldsHTML += '<select name="contentCulture" id="contentCulture" class="swal2-input">';

						for (var key in cultureList)
							fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';

				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label><input type="checkbox" name="contentReplace" id="contentReplace" checked> Intégrer le texte dans l\'éditeur</label>';
				fieldsHTML += '</div>';

				resolveFields = ['contentSubject', 'contentCulture', 'contentReplace'];
				break;

				case 'introduction':

					var title = 'Rédiger une introduction';
				
					fieldsHTML += '<div class="col-12 text-left">';
						fieldsHTML += '<label for="contentSubject">Sujet de l\'article</label>';
						fieldsHTML += '<input type="text" name="contentSubject" id="contentSubject" placeholder="Ex: Pourquoi créer un site internet pour son entreprise" class="swal2-input">';
					fieldsHTML += '</div>';

					fieldsHTML += '<div class="col-12 text-left">';
						fieldsHTML += '<label for="contentTone">Ton à employer</label>';
						fieldsHTML += '<select name="contentTone" id="contentTone" class="swal2-input">';

							for (var key in i18n.tones)
								fieldsHTML += '<option value="'+ key +'">'+ i18n.tones[key].title +'</option>';

						fieldsHTML += '</select>';
					fieldsHTML += '</div>';
					
					fieldsHTML += '<div class="col-12 text-left">';
						fieldsHTML += '<label for="contentCulture">Langue de rédaction</label>';
						fieldsHTML += '<select name="contentCulture" id="contentCulture" class="swal2-input">';

							for (var key in cultureList)
								fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

						fieldsHTML += '</select>';
					fieldsHTML += '</div>';
					
					fieldsHTML += '<div class="col-12 text-left">';
						fieldsHTML += '<label><input type="checkbox" name="contentReplace" id="contentReplace" checked> Intégrer le texte dans l\'éditeur</label>';
					fieldsHTML += '</div>';

					resolveFields = ['contentSubject', 'contentTone', 'contentCulture', 'contentReplace'];
					break;

			case 'article':
			
				var title = 'Rédiger un article';
			
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentSubject">Sujet de l\'article</label>';
					fieldsHTML += '<input type="text" name="contentSubject" id="contentSubject" placeholder="Ex: Pourquoi créer un site internet pour son entreprise" class="swal2-input">';
				fieldsHTML += '</div>';

				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentTone">Ton à employer</label>';
					fieldsHTML += '<select name="contentTone" id="contentTone" class="swal2-input">';

						for (var key in i18n.tones)
							fieldsHTML += '<option value="'+ key +'">'+ i18n.tones[key].title +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';
				
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentCulture">Langue de rédaction</label>';
					fieldsHTML += '<select name="contentCulture" id="contentCulture" class="swal2-input">';

						for (var key in cultureList)
							fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';
				
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label><input type="checkbox" name="contentReplace" id="contentReplace" checked> Intégrer le texte dans l\'éditeur</label>';
				fieldsHTML += '</div>';

				resolveFields = ['contentSubject', 'contentTone', 'contentCulture', 'contentReplace'];
				break;

			case 'outline':
			
				var title = 'Structure d\'un article';
			
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentSubject">Sujet</label>';
					fieldsHTML += '<input type="text" name="contentSubject" id="contentSubject" placeholder="Ex: La création de sites internet" class="swal2-input">';
				fieldsHTML += '</div>';

				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentCulture">Langue de rédaction</label>';
					fieldsHTML += '<select name="contentCulture" id="contentCulture" class="swal2-input">';

						for (var key in cultureList)
							fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';

				resolveFields = ['contentSubject', 'contentCulture'];

				break;

			case 'keywords':
			
				var title = 'Trouver des mots-clés';
			
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentSubject">Sujet</label>';
					fieldsHTML += '<input type="text" name="contentSubject" id="contentSubject" placeholder="Ex: La création de sites internet" class="swal2-input">';
				fieldsHTML += '</div>';

				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentCulture">Langue de rédaction</label>';
					fieldsHTML += '<select name="contentCulture" id="contentCulture" class="swal2-input">';

						for (var key in cultureList)
							fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';

				resolveFields = ['contentSubject', 'contentCulture'];

				break;

			case 'rephrase':
				var title = 'Reformuler le texte';

				fieldsHTML += '<p>Le texte de l\'éditeur sera utilisé et remplacé avec la nouvelle formulation';

				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentTone">Ton à employer</label>';
					fieldsHTML += '<select name="contentTone" id="contentTone" class="swal2-input">';

						for (var key in i18n.tones)
							fieldsHTML += '<option value="'+ key +'">'+ i18n.tones[key].title +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';
				
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentCulture">Langue de rédaction</label>';
					fieldsHTML += '<select name="contentCulture" id="contentCulture" class="swal2-input">';

						for (var key in cultureList)
							fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';

				fieldsHTML += '<p class="col-12 text-left mt-3">Vous avez la possibilité de remplacer un mot clé par un autre. (optionnel)</p>';
				fieldsHTML += '<div class="col-6 text-left">';
					fieldsHTML += '<label for="contentCulture">Mot-clé d\'origine</label>';
					fieldsHTML += '<input name="contentKeyword1" id="contentKeyword1" class="swal2-input" />';
				fieldsHTML += '</div>';
				fieldsHTML += '<div class="col-6 text-left">';
					fieldsHTML += '<label for="contentCulture">Mot-clé de remplacement</label>';
					fieldsHTML += '<input name="contentKeyword2" id="contentKeyword2" class="swal2-input" />';
				fieldsHTML += '</div>';

				resolveFields = ['contentTone', 'contentCulture', 'contentKeyword1', 'contentKeyword2'];

				break;

			case 'product':
			
				var title = 'Rédiger la description d\'un produit';
			
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentProductName">Nom du produit</label>';
					fieldsHTML += '<input type="text" name="contentProductName" id="contentProductName" class="swal2-input">';
				fieldsHTML += '</div>';
			
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentBrandName">Marque du produit</label>';
					fieldsHTML += '<input type="text" name="contentBrandName" id="contentBrandName" class="swal2-input">';
				fieldsHTML += '</div>';
			
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentFeature">Fonction ou utilité du produit (en quelques mots)</label>';
					fieldsHTML += '<input type="text" name="contentFeature" id="contentFeature" class="swal2-input">';
				fieldsHTML += '</div>';
				
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label for="contentCulture">Langue de rédaction</label>';
					fieldsHTML += '<select name="contentCulture" id="contentCulture" class="swal2-input">';

						for (var key in cultureList)
							fieldsHTML += '<option value="'+ key +'">'+ cultureList[key] +'</option>';

					fieldsHTML += '</select>';
				fieldsHTML += '</div>';
				
				fieldsHTML += '<div class="col-12 text-left">';
					fieldsHTML += '<label><input type="checkbox" name="contentReplace" id="contentReplace" checked> Intégrer le texte dans l\'éditeur</label>';
				fieldsHTML += '</div>';

				resolveFields = ['contentProductName', 'contentBrandName', 'contentFeature', 'contentCulture', 'contentReplace'];
				break;

			default:
				return false;
		}

		fieldsHTML += '<div class="col-12 ai-recommendations-link">';
			fieldsHTML += '<a href="https://qaniit.com/fr/articles/la-generation-de-contenus-via-l-ia" target="_blank">💡 Lire nos recommandations concernant la rédaction automatique</a>';
		fieldsHTML += '</div>';

		Swal.fire({
			title: title,
			showCancelButton: true,
			cancelButtonText: i18n.general.cancel,
			confirmButtonText: i18n.general.validate,
			html: '<div class="row">' + fieldsHTML + '</div>',
			preConfirm: function () {
				return new Promise(function (resolve) {
					var result = {};

					for (var key in resolveFields)
					{
						if ($('#' + resolveFields[key]).is(':checkbox'))
							result[resolveFields[key]] = $('#' + resolveFields[key]).is(':checked') ? 1 : 0;
						else
							result[resolveFields[key]] = $('#' + resolveFields[key]).val();
					}

					resolve(result);
				});
			}
		}).then((result) => {
			if (result.value)
			{
				displayLoader();

				var data = result.value;
				data.mode = mode;
				data.csrf_generateContent = csrf;

				if (mode == 'rephrase')
					data.contentText = CKEDITOR.instances['field_blocContent_' + data.contentCulture].getData();

				$.getJSON(ajaxURL + 'cms_generateContent', data, function(json) {
					if (json.error)
					{
						showError(json.data.message);
						hideLoader()
					}
					else
					{
						hideLoader();

						if (json.data.contentReplace && targetField)
						{
							CKEDITOR.instances[targetField + '_' + data.contentCulture].setData(json.data.html);
						}
						else
						{
							Swal.fire({
								position: 'center',
								text: json.data.message,
								input: 'textarea',
								inputValue: json.data.content,
								showCancelButton: false,
								confirmButtonText: i18n.general.close,
								backdrop: true,
								customClass: 'ai-result-swal-popup'
							})
						}

						$('.quota-ai-content .current').text(json.data.quota.current);
					}
				});
			}
		});
	});
});

// https://stackoverflow.com/a/40658647/1320311
jQuery.fn.isInViewport = function() {
  var elementTop = jQuery(this).offset().top;
  var elementBottom = elementTop + jQuery(this).outerHeight();

  var viewportTop = jQuery(window).scrollTop();
  var viewportBottom = viewportTop + jQuery(window).height();

  return elementBottom > viewportTop && elementTop < viewportBottom;
};

/** Open admin page in popup.
 * @param (string) titre : title of popup.
 * @param (string) url : url of content. */
function openAdminPopup(titre, url, id)
{
	var content = '<iframe src="'+ url +'" width="100%" height="100%"></iframe>';
	content += '<div class="cms-edit-loader"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i><span>'+ i18n.editor.misc.loading +'</span></div>';
	openPopup(titre, content, id);
} // openAdminPopup()

/** Open ui dialog.
 * @param (string) title : Popup title.
 * @param (string) content : Popup content. */
function openPopup(title, content, id)
{
	var id = typeof id != "undefined" ? id : 'cmsDialog';

	$('<div/>', {
		'id': id,
		'class': 'cmsDialog'
	}).appendTo('body');
	$('#' + id).html(content);
	$('#' + id).dialog({
		show: 'blind',
		hide: 'blind',
		title: null,
		zIndex : 20,
		resizable: true
	});
} // openPopup()

/** Close ui dialog. */
function closePopup(id)
{
	if (typeof id === "undefined")
		var id = 'cmsDialog';

	if ($('#'+id).dialog("isOpen") == true)
	{
		$('#' + id).dialog('close');
		$('#' + id).dialog("destroy");
		$('#' + id).remove();
	}
} // closePopup()

function showMessage(message, reload, options) {
	displaySweetAlert('info', message, reload, options);
} // showMessage()

function showReloadMessage(message, reload = true, options = {})
{
	var baseProps = {
		position: 'center',
		backdrop: true,
	};

	var options = jQuery.extend(baseProps, options);
	displaySweetAlert('info', message, reload, options);
} // showReloadMessage()

function showSuccess(message, reload, options) {
	displaySweetAlert('success', message, reload, options);
} // showSuccess()

function showWarning(message, reload, options) {
	displaySweetAlert('warning', message, reload, options);
} // showWarning()

function showError(message, reload, options) {
	displaySweetAlert('error', message, reload, options);
} // showError()

function displaySweetAlert(type, message, redirect = false, options = {})
{
	var baseProps = {
		position: 'top-end',
	  type: type,
	  html: message,
		backdrop: false,
		confirmButtonText: i18n.general.close,
	  //timer: 3500,
		customClass: 'custom-swal-popup',
		onClose: function() {
			if (redirect === true)
				window.location.reload();
			else if (redirect !== false)
				window.location.href = redirect;
		}
	};

	Swal.fire(jQuery.extend(baseProps, options));

	if (redirect === true)
	{
		window.setTimeout(function(){
	    window.location.reload();
		}, 5000);
	}
	else if (redirect !== false)
	{
		window.setTimeout(function(){
	    window.location.href = redirect;
		}, 5000);
	}
} // displaySweetAlert()

/** Retourne un tableau JSON contenant tous les champs du formulaire passé en paramètre.
 * @param (string) formID : Identifiant du formulaire. */
function getFormFields(formID)
{
	var formFields		= {};

	$(formID).find('input[type!="checkbox"]').each(function() {
		formFields[$(this).attr('name')]	= $(this).val();
	});

	$(formID).find('textarea').each(function() {
		formFields[$(this).attr('name')]	= $(this).val();
	});

	$(formID).find('select').each(function() {
		formFields[$(this).attr('name')]	= $(this).val();
	});

	$(formID).find('input[type="checkbox"]').each(function() {
		formFields[$(this).attr('name')]	= ( $(this).attr('checked') == 'checked' ) ? 1 : 0;
	});

	$(formID).find('input[type="radio"]:checked').each(function() {
		formFields[$(this).attr('name')]	= $(this).val();
	});

	return formFields;
} // getFormFields()

/** Vérification que l'email passé en paramètre est un email valide.
 * @param (string) email : Email à tester. */
function checkEmail(email)
{
	var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);
	return pattern.test(email);
} // isValidEmailAddress()

/** Return URL parameter. */
function getURLParameter(name, url) {
	var url = typeof url !== 'undefined' ? url : location.search;
  return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(url)||[,""])[1].replace(/\+/g, '%20'))||null
} // getURLParameter()

function replaceUrlParameter(url, parameter, value) {
	let parsedUrl = new URL(url);
	let searchParams = parsedUrl.searchParams;
	searchParams.set(parameter, value);
	parsedUrl.search = searchParams.toString();
	return parsedUrl.toString();
} // replaceUrlParameter()

/** Decode escaped string. */
function htmlDecode(input) {
	var doc = new DOMParser().parseFromString(input, "text/html");
	return doc.documentElement.textContent;
} // htmlDecode()

function getDomainFromURL(url, protocol)
{
	var protocol = protocol ? protocol : false;
  var a = document.createElement('a');
	a.href = url;
  return (protocol ? a.protocol + '//' : null) + a.hostname;
} // getDomainFromURL()

/** Clean a string by removing special characters.
 * @param (string) string : String to clean. */
function cleanString(string)
{
	var cleanString	= '';

	$.ajax({
		url: ajaxURL +'cms_getCleanString',
		type: "POST",
		data: 'string='+ string,
		async: false,
		dataType: 'json',
		success: function (json) {
			cleanString = json.data.message;
		}
	});

	return cleanString;
} // cleanString()

/** Scroll to an anchor in page */
function scrollToAnchor(to, speed, margin)
{
  var speed = speed === undefined ? 750 : speed;
	var margin = margin === undefined ? 20 : margin;
  var offset = 0;

  if(!$(to).is(':visible'))
  {
    $(to).show(0, function() {
      offset = $(to).offset();
      $(to).hide();
    });
  }
  else
  {
    offset = $(to).offset();
  }

  $('html, body').animate({
		scrollTop: (offset.top - margin)
	}, speed);
  return false;
} // scrollToAnchor()

/** Launch pictures resizing on page. */
function resizeItems()
{
	var isIE = /*@cc_on!@*/false || !!document.documentMode; // Internet Explorer 6-11
	var isEdge = !isIE && !!window.StyleMedia; // Edge 20+

	if (isIE || isEdge)
	{
		$('.resizeItem').each(function() {
			// avoid to launch 2 times resizing when using lazyload.
			if ($(this).find('.lazyload').length) return;
			resizeItem($(this));
		});
	}
} // resizeItems()

/** Resize picture to fit into a container */
function resizeItem(container)
{
  // make image not overflow container
  var overflow = typeof container.data('overflow') !== 'undefined' ? container.data('overflow') : true;

  // no overflow for portrait pictures
  var preservePortrait = typeof container.data('preserveportrait') !== 'undefined' ? container.data('preserveportrait') : true;

  // if true not enlarge images smaller than container
  var enlarge = typeof container.data('enlarge') !== 'undefined' ? container.data('enlarge') : true;

  // if true, make image visible after resizing
  var opacity = typeof container.data('opacity') !== 'undefined' ? container.data('opacity') : true;

  // if defined, container size will be calculated.
  var ratio = typeof container.data('ratio') !== 'undefined' ? container.data('ratio') : false;


  if (ratio)
  {
  	ratio = ratio.split('/');

		if (container.width())
		{
  		var elemWidth = container.width();
			var elemHeight = elemWidth * ratio[1] / ratio[0];
			container.css('height', elemHeight);
		}
		else
		{
			var elemHeight = container.height();
			var elemWidth = elemHeight * ratio[0] / ratio[1];
			container.css('width', elemWidth);
		}
  }
  else
  {
  	var elemWidth = container.width();
		var elemHeight = container.height();
  }

  var elemRatio = elemWidth / elemHeight;

  container.children('img, iframe, video').each(function() {
    // get original picture size.
    $(this).css({ width:'auto', height: 'auto' });

    var pictureWidth = $(this).width();
    var pictureHeight = $(this).height();
    var pictureRatio = pictureWidth / pictureHeight;

    if (isNaN(pictureRatio)) // fix when item is in display none
      $(this).css({ width:'100%', 'min-height': '100%' });
    else if (enlarge == false && pictureWidth < elemWidth && pictureHeight < elemHeight) // picture smaller than container
      $(this).css({ width:'auto', height: 'auto' });
    else if (overflow == false)
    {
      // No overflow
      if (pictureRatio < elemRatio)
        $(this).css({ width: 'auto', height:'100%' });
      else
        $(this).css({ width:'100%', height: 'auto' });
    }
    else if (pictureRatio < 1 && preservePortrait) // Portrait picture with no overflow
      $(this).css({ width: 'auto', height:'100%' });
    else if (pictureRatio < elemRatio) // Landscape
      $(this).css({ width:'100%', height: 'auto' });
    else // Portrait
      $(this).css({ width: 'auto', height:'100%' });

    if (opacity) $(this).animate({'opacity': 1}, 800);
  });
} // resizeItem()

/* Fix on item ratio to update size on portrait pictures. */
function checkRatioItem(container, item)
{
	// Handle dynamic ratio
	if (container.attr('data-ratio') === 'calc') {
		let padding = parseInt(container.attr('data-ratio-height')) / parseInt(container.attr('data-ratio-width')) * 100;
		console.log(padding);
		container.css('padding-bottom', padding + '%');
	}

	var blockRatio = parseInt(container.css('padding-bottom')) / parseInt(container.width()) * 100;
	var itemRatio = parseInt(item.height()) / parseInt(item.width()) * 100;

	if (itemRatio > blockRatio)
	{
		item.css({
			width: '100%',
			height: 'auto'
		});
	}
} // checkRatioItem()

/** Swap 2 items. */
function swapElements(elm1, elm2)
{
	$(elm1).slideUp('slow', function() {
	  $(this).insertBefore(elm2).slideDown('slow');
	});
} // swapElements()

/** Update count items for blocs trash.
 * @param (int) count: count blocs. */
function updateTrashCount(count)
{
	$('#blocsInTrash .value').text(count);

	if (count == 0)
		$('#blocsInTrash').slideUp('slow');
	else
		$('#blocsInTrash').slideDown('slow');
} // updateTrashCount()

/** Pages sections design */
function sectionsStretching()
{
	// Row background stretching
	$('.cms-section[data-stretching="row"]').each(function() {
		let offsetLeft = $(this).offset().left;

		$(this).find('> .background, > .border-effect').css({
			'left': -offsetLeft
		});
	});

	// Row background and content stretching
	$('.cms-section[data-stretching="content"]').each(function() {
		let id = '#' + $(this).attr('id');

		$(id).css('left', 'auto');

		//let offsetLeft = $(this).offset().left;
		let offsetLeft = ($('#wrapper').offset().left + parseInt($('#wrapper').css('paddingLeft'))) - parseInt($('#content').css('paddingLeft'));
		let leftPosition = $(id).css('left');

		$(id).css({
			'left': -offsetLeft,
			'width': '100vw'
		});
  });
} // sectionsStretching()

function previewUploadedFile(file, inputName = 'file', container)
{
	const reader = new FileReader();
	var callback = container.attr('data-callback');

	// Check file type
	var fileTypes = container.attr('data-allowedfiles');
	var fileTypes = fileTypes.split(',');

	if (fileTypes.indexOf(file.type) == -1)
	{
		Swal.fire({
			text: 'Le fichier '+ file.name +' n\'est pas un fichier autorisé',
			type: 'error'
		});
		return false;
	}

	// Check number of files allowed to upload
	var countFilesItem = container.find('.files-count span');
	var countFiles = parseInt(countFilesItem.text());
	var maximumFiles = container.attr('data-maximum-files') ? parseInt(container.attr('data-maximum-files')) : false;

	if (maximumFiles && countFiles >= container.attr('data-maximum-files'))
	{
		Swal.fire({
			text: 'Le fichier '+ file.name +' ne peut pas être ajouté car vous avez atteint le nombre maximum de fichiers',
			type: 'error'
		});
		return false;
	}

	// Update files count
	countFilesItem.text(countFiles + 1);

	reader.addEventListener("load", function () {
		// Resize picture to reduce filesize
		var options = {
			outputMaxWidth: 2560,
			outputMaxHeight: 2560,
			outputFormat: 'image/jpeg',
			outputQuality: 0.9
		};

		const canvas = document.createElement('canvas');
		const context = canvas.getContext('2d');
		const imgObj = new Image();
		imgObj.src = reader.result;

		if (file.type == 'image/jpeg')
		{
			imgObj.onload = function() {
				// JPEG pictures are resized to reduce file size
				context.drawImage(this, 0, 0, canvas.width, canvas.height);

				const imgWidth = imgObj.width;
				const imgHeight = imgObj.height;
				let width;
				let height;

				if (imgWidth > options.outputMaxWidth || imgHeight > options.outputMaxHeight)
				{
					if (imgWidth > imgHeight)
					{
						width = options.outputMaxWidth;
						height = Math.round((imgHeight / imgWidth) * width);
					}
					else
					{
						height = options.outputMaxHeight;
						width = Math.round((imgWidth / imgHeight) * height);
					}
				}
				else
				{
					width = imgWidth;
					height = imgHeight;
				}

				canvas.width = width;
				canvas.height = height;

				context.drawImage(imgObj, 0, 0, width, height);
				var base64 = canvas.toDataURL(options.outputFormat, options.outputQuality);
				var preview = '<div class="picture" data-filetype="'+file.type+'" style="background-image: url(\''+ base64 +'\');">';
					preview += '<input type="hidden" name="'+ inputName +'" value="'+ base64 +'" />';
					preview += '<input type="hidden" name="names_'+ inputName +'" value="'+ file.name +'" />';
					preview += '<div class="delete"><i class="fa fa-trash fa-lg fa-fw"></i></div>';
				preview += '</div>';

				var previewItem = $(preview);
	
				container.find('.preview').append(previewItem);
	
				previewItem.animate({
					opacity: 1
				}, 1000);
				
				if (callback !== undefined)
					window[callback]();
			};
		}
		else if (file.type == 'image/gif' || file.type == 'image/png' || file.type == 'image/bmp')
		{
			imgObj.onload = function() {
				var base64 = reader.result;
				var preview = '<div class="picture" data-filetype="'+file.type+'" style="background-image: url(\''+ base64 +'\');">';
					preview += '<input type="hidden" name="'+ inputName +'" value="'+ base64 +'" />';
					preview += '<input type="hidden" name="names_'+ inputName +'" value="'+ file.name +'" />';
					preview += '<div class="delete"><i class="fa fa-trash fa-lg fa-fw"></i></div>';
				preview += '</div>';

				var previewItem = $(preview);
	
				container.find('.preview').append(previewItem);
	
				previewItem.animate({
					opacity: 1
				}, 1000);
				
				if (callback !== undefined)
					window[callback]();
			};
		}
		else
		{
			var base64 = reader.result;
			var preview = '<div class="picture" data-filetype="'+file.type+'">';
				preview += '<input type="hidden" name="'+ inputName +'" value="'+ base64 +'" />';
				preview += '<input type="hidden" name="names_'+ inputName +'" value="'+ file.name +'" />';
				preview += '<div class="delete"><i class="fa fa-trash fa-lg fa-fw"></i></div>';
			preview += '</div>';

			var previewItem = $(preview);

			container.find('.preview').append(previewItem);

			previewItem.animate({
				opacity: 1
			}, 1000);
			
			if (callback !== undefined)
				window[callback]();
		}
	}, false);

	if (file)
		reader.readAsDataURL(file);
} // previewUploadedFile()

function toggleLoader(container = false, message = false)
{
	if ($('.cms-edit-loader').is(':visible'))
		hideLoader();
	else
		displayLoader(container, message);
} // toggleLoader()

function displayLoader(container, message)
{
	container = container == false ? 'body' : container;
	$('.cms-edit-loader').appendTo(container);

	if (container != 'body')
	{
		$(container).css('position', 'relative');
		$('.cms-edit-loader').addClass('used-as-child');
	}
	else
	{
		$('body').css('overflow', 'hidden');
	}

	if (message != false)
		$('.cms-edit-loader').find('.message').text(message);
	else
		$('.cms-edit-loader').find('.message').text(i18n.editor.misc.loading);

	$('.cms-edit-loader').css('display', 'flex').hide().fadeIn('fast');
} // displayLoader()

function hideLoader()
{
	$('.cms-edit-loader').fadeOut('fast');
	$('.cms-edit-loader').removeClass('used-as-child');
	$('body').css('overflow', 'auto');
} // hideLoader()

function lazyload()
{
	$('img.unveil, img.lazyload').unveil(500, function() {
		$(this).addClass('loaded');
		$(this).on('load', function() {
			if ($(this).parents().hasClass('resizePicture'))
				resizePicture($(this).parent());
		});
	});
} // lazyload()

function activateDisabledLinks() {
	$('.disabled-before-load').removeClass('disabled-before-load');
} // activateDisabledLinks()

function copyToClipboard(item)
{
  item[0].select();
  item[0].setSelectionRange(0, 99999);
  document.execCommand("copy");

	showSuccess('Texte copié dans le presse papier');
} // copyToClipboard()

function isInViewport(node)
{
  var rect = node.getBoundingClientRect()
  return (
    (rect.height > 0 || rect.width > 0) &&
    rect.bottom >= 0 &&
    rect.right >= 0 &&
    rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.left <= (window.innerWidth || document.documentElement.clientWidth)
  )
} // isInViewport()

/** Move parallax backgrounds */
function initParallaxBackground()
{
	$('.parallax-background').each(function() {
		var item = $(this);
		var background = item.css('background-image');
		var radius = item.css('border-radius');
		var url = background.replace('url(', '').replace(')', '').replace(/\"/gi, "");

		item.parallax({
			mirrorContainer: '#wrapper',
			imageSrc: url,
			speed: 0.5,
			androidDisabled: false,
			iosDisabled: false,
			iosFix: false,
			androidFix: false
		});

		item.css('background-image', 'none');

		/* Generate an ID and attach it to parallax and original item
		 * This allow to update parallax if needed based on original content */
		var id = getRandString();
		var mirror = $('.parallax-mirror:not([data-parallax-id])');

		mirror.attr('data-parallax-id', id);
		item.attr('data-parallax-id', id);

		if (radius)
			mirror.css('border-radius', radius);
	});
} // initParallaxBackground()

function bannerSlider()
{
  if ($('.banner-slider').length)
  {
		var bannerSlider = $('.banner-slider');
    bannerSlider.owlCarousel({
      items: 1,
      loop: true,
      margin: 10,
      nav: false,
      dots: false,
      lazyLoad: true,
      autoplay: true,
      autoplayTimeout: bannerSlider.attr('data-speed'),
      autoplayHoverPause: true,
      animateIn: bannerSlider.attr('data-effect-in'),
      animateOut: bannerSlider.attr('data-effect-out')
    });
  }
} // bannerSlider()

function activateEffectsOnBlocks()
{
	AOS.init({
		once: true,
		offset: 200,
	  delay: 100,
	  duration: 600
	});
} // activateEffectsOnBlocks()

// https://stackoverflow.com/a/1909508/1320311
function delay(fn, ms) {
  let timer = 0
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(fn.bind(this, ...args), ms || 0)
  }
} // delay()

/** Apply same filter from picture source to lightbox picture */
function filterOnLightboxPictures()
{
	$(document).on('lity:ready', function(event, instance) {
		var element = instance.element();
		var opener = instance.opener();
		var filter = opener.attr('data-lightbox-filter');

		if (filter)
			element.find('.lity-container img').addClass(filter);
	});
} // filterOnLightboxPictures()

function lazyLoading()
{
	$('img.unveil, img.lazyload').not('.loaded').unveil(200, function() {
		$(this).addClass('loaded');
		$(this).on('load', function() {

			// if parent has resizePicture class, launch resizing after load
			if ($(this).parents().hasClass('resizePicture'))
				resizePicture($(this).parent());

			refreshParallax();
		});
	});
} // lazyLoading()

/* Fix parallax effect when changes occurs on page */
function refreshParallax(delay)
{
	if (delay !== undefined)
	{
		$('.parallax-mirror').fadeOut('fast');
		setTimeout(() => {
			$(window).trigger('resize').trigger('scroll');
			$('.parallax-mirror').fadeIn('fast');
		}, delay);
	}
	else
	{
		$(window).trigger('resize').trigger('scroll');
	}
} // refreshParallax()

function hexToRgb(hex)
{
  // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
  var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  hex = hex.replace(shorthandRegex, function(m, r, g, b) {
    return r + r + g + g + b + b;
  });

  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
    r: parseInt(result[1], 16),
    g: parseInt(result[2], 16),
    b: parseInt(result[3], 16)
  } : null;
} // hexToRgb()

function renderCaptcha()
{
	$('.h-captcha').each(function() {
		var widgetID = hcaptcha.render($(this).attr('id'), { sitekey: $(this).attr('data-sitekey') });
	});
} // renderCaptcha()

function updateStickyHeaderMargin(value)
{
	if (isNaN(value))
		return false;

	stickyHeaderMargin += value;
	$('table').stickyTableHeaders({fixedOffset: stickyHeaderMargin});
} // updateStickyHeaderMargin()

function getAvailableCultures()
{
	var cultureList = [];

	for (let i = 0; i < availableCultures.length; i++)
		cultureList[availableCultures[i]] = i18n.languages[availableCultures[i]];

	return cultureList;
} // getAvailableCultures()

function getRandString(length)
{
	if (length === undefined)
		var length = 8;

	var symbols = "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	var password = '';

	for (var i = 0 ; i < length ; i++)
		password += symbols[parseInt(Math.random() * symbols.length)];

	return password;
} // getRandString()

function translateContent(sourceContent, sourceType, sourceID, sourceCulture, csrf, fieldsList, contentReplace)
{
	if (typeof contentReplace === "undefined")
		var contentReplace = 1;

	$.post(ajaxURL + 'cms_translateContent', {
		sourceContent: sourceContent,
		sourceType: sourceType,
		sourceID: sourceID,
		sourceCulture: sourceCulture,
		fields: fieldsList,
		contentReplace: contentReplace,
		csrf_translateContent: csrf
	}, function(json) {
		if (json.error)
		{
			showError(json.data.message);
			hideLoader();
		}
		else
		{
			if (sourceContent == 'form')
			{
				for (var culture in json.data.fields)
				{
					for (let j = 0; j < json.data.fields[culture].length; j++)
					{
						let field = json.data.fields[culture][j];

						if (field['value'])
						{
							if (field['type'] == 'ckeditor')
								CKEDITOR.instances[field['id']].setData(field['value']);
							else
								$('#' + field['id']).val(field['value']);
						}
					}
				}
			}
			else
			{
				if (sourceType == 'bloc')
				{
					// Do same action as a bloc update
					window.parent.postMessage({
						action: 'updateBloc',
						blocID: json.data.blocID,
						message: json.data.message,
						reload: json.data.reload,
						section: json.data.section,
						contents: json.data.contents
					}, '*');
				}
				else
				{
					showMessage(json.data.message);
				}
			}

			// Display quota
			$('.quota-translations .current').text(json.data.quota.current);

			hideLoader();
		}
	}, 'json');
} // translateContent()

// Prevent unsaved changes
var unsavedChanges = false;
var formSubmitted = false;

$(function() {
	$('form[data-unsavedChanges="true"]').on('change', 'input[type="text"], input[type="password"], textarea, select', function() {
	  unsavedChanges = true;
	});
  $('form[data-unsavedChanges="true"]').on('submit', function() { formSubmitted = true; });
});

function unloadPage()
{
  if (unsavedChanges && !formSubmitted)
    return "You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?";
} // unloadPage()

window.onbeforeunload = unloadPage;

/**
 * jQuery Unveil
 * A very lightweight jQuery plugin to lazy load images
 * http://luis-almeida.github.com/unveil
 *
 * Licensed under the MIT license.
 * Copyright 2013 Luís Almeida
 * https://github.com/luis-almeida
 */

;(function($) {

  $.fn.unveil = function(threshold, callback) {

    var $w = $(window),
        th = threshold || 0,
        retina = window.devicePixelRatio > 1,
        attrib = retina? "data-src-retina" : "data-src",
        images = this,
        loaded;

    this.one("unveil", function() {
      var source = this.getAttribute(attrib);
      source = source || this.getAttribute("data-src");
      if (source) {
        this.setAttribute("src", source);
        if (typeof callback === "function") callback.call(this);
      }
    });

    function unveil() {
      var inview = images.filter(function() {
        var $e = $(this);
        if ($e.is(":hidden")) return;

        var wt = $w.scrollTop(),
            wb = wt + $w.height(),
            et = $e.offset().top,
            eb = et + $e.height();

        return eb >= wt - th && et <= wb + th;
      });

      loaded = inview.trigger("unveil");
      images = images.not(loaded);
    }

    $w.on("scroll.unveil resize.unveil lookup.unveil", unveil);

    unveil();

    return this;

  };

})(window.jQuery || window.Zepto);

/**
 * Copyright Marc J. Schmidt. See the LICENSE file at the top-level
 * directory of this distribution and at
 * https://github.com/marcj/css-element-queries/blob/master/LICENSE.
 */
;
(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(factory);
    } else if (typeof exports === "object") {
        module.exports = factory();
    } else {
        root.ResizeSensor = factory();
    }
}(typeof window !== 'undefined' ? window : this, function () {

    // Make sure it does not throw in a SSR (Server Side Rendering) situation
    if (typeof window === "undefined") {
        return null;
    }
    // Only used for the dirty checking, so the event callback count is limited to max 1 call per fps per sensor.
    // In combination with the event based resize sensor this saves cpu time, because the sensor is too fast and
    // would generate too many unnecessary events.
    var requestAnimationFrame = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        function (fn) {
            return window.setTimeout(fn, 20);
        };

    /**
     * Iterate over each of the provided element(s).
     *
     * @param {HTMLElement|HTMLElement[]} elements
     * @param {Function}                  callback
     */
    function forEachElement(elements, callback){
        var elementsType = Object.prototype.toString.call(elements);
        var isCollectionTyped = ('[object Array]' === elementsType
            || ('[object NodeList]' === elementsType)
            || ('[object HTMLCollection]' === elementsType)
            || ('[object Object]' === elementsType)
            || ('undefined' !== typeof jQuery && elements instanceof jQuery) //jquery
            || ('undefined' !== typeof Elements && elements instanceof Elements) //mootools
        );
        var i = 0, j = elements.length;
        if (isCollectionTyped) {
            for (; i < j; i++) {
                callback(elements[i]);
            }
        } else {
            callback(elements);
        }
    }

    /**
     * Class for dimension change detection.
     *
     * @param {Element|Element[]|Elements|jQuery} element
     * @param {Function} callback
     *
     * @constructor
     */
    var ResizeSensor = function(element, callback) {
        /**
         *
         * @constructor
         */
        function EventQueue() {
            var q = [];
            this.add = function(ev) {
                q.push(ev);
            };

            var i, j;
            this.call = function() {
                for (i = 0, j = q.length; i < j; i++) {
                    q[i].call();
                }
            };

            this.remove = function(ev) {
                var newQueue = [];
                for(i = 0, j = q.length; i < j; i++) {
                    if(q[i] !== ev) newQueue.push(q[i]);
                }
                q = newQueue;
            }

            this.length = function() {
                return q.length;
            }
        }

        /**
         *
         * @param {HTMLElement} element
         * @param {Function}    resized
         */
        function attachResizeEvent(element, resized) {
            if (!element) return;
            if (element.resizedAttached) {
                element.resizedAttached.add(resized);
                return;
            }

            element.resizedAttached = new EventQueue();
            element.resizedAttached.add(resized);

            element.resizeSensor = document.createElement('div');
            element.resizeSensor.className = 'resize-sensor';
            var style = 'position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;';
            var styleChild = 'position: absolute; left: 0; top: 0; transition: 0s;';

            element.resizeSensor.style.cssText = style;
            element.resizeSensor.innerHTML =
                '<div class="resize-sensor-expand" style="' + style + '">' +
                    '<div style="' + styleChild + '"></div>' +
                '</div>' +
                '<div class="resize-sensor-shrink" style="' + style + '">' +
                    '<div style="' + styleChild + ' width: 200%; height: 200%"></div>' +
                '</div>';
            element.appendChild(element.resizeSensor);

            if (element.resizeSensor.offsetParent !== element) {
                element.style.position = 'relative';
            }

            var expand = element.resizeSensor.childNodes[0];
            var expandChild = expand.childNodes[0];
            var shrink = element.resizeSensor.childNodes[1];
            var dirty, rafId, newWidth, newHeight;
            var lastWidth = element.offsetWidth;
            var lastHeight = element.offsetHeight;

            var reset = function() {
                expandChild.style.width = '100000px';
                expandChild.style.height = '100000px';

                expand.scrollLeft = 100000;
                expand.scrollTop = 100000;

                shrink.scrollLeft = 100000;
                shrink.scrollTop = 100000;
            };

            reset();

            var onResized = function() {
                rafId = 0;

                if (!dirty) return;

                lastWidth = newWidth;
                lastHeight = newHeight;

                if (element.resizedAttached) {
                    element.resizedAttached.call();
                }
            };

            var onScroll = function() {
                newWidth = element.offsetWidth;
                newHeight = element.offsetHeight;
                dirty = newWidth != lastWidth || newHeight != lastHeight;

                if (dirty && !rafId) {
                    rafId = requestAnimationFrame(onResized);
                }

                reset();
            };

            var addEvent = function(el, name, cb) {
                if (el.attachEvent) {
                    el.attachEvent('on' + name, cb);
                } else {
                    el.addEventListener(name, cb);
                }
            };

            addEvent(expand, 'scroll', onScroll);
            addEvent(shrink, 'scroll', onScroll);
        }

        forEachElement(element, function(elem){
            attachResizeEvent(elem, callback);
        });

        this.detach = function(ev) {
            ResizeSensor.detach(element, ev);
        };
    };

    ResizeSensor.detach = function(element, ev) {
        forEachElement(element, function(elem){
            if (!elem) return
            if(elem.resizedAttached && typeof ev == "function"){
                elem.resizedAttached.remove(ev);
                if(elem.resizedAttached.length()) return;
            }
            if (elem.resizeSensor) {
                if (elem.contains(elem.resizeSensor)) {
                    elem.removeChild(elem.resizeSensor);
                }
                delete elem.resizeSensor;
                delete elem.resizedAttached;
            }
        });
    };

    return ResizeSensor;

}));

/**
 * Copyright Marc J. Schmidt. See the LICENSE file at the top-level
 * directory of this distribution and at
 * https://github.com/marcj/css-element-queries/blob/master/LICENSE.
 */
;
(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(['./ResizeSensor.js'], factory);
    } else if (typeof exports === "object") {
        module.exports = factory(require('./ResizeSensor.js'));
    } else {
        root.ElementQueries = factory(root.ResizeSensor);
        root.ElementQueries.listen();
    }
}(typeof window !== 'undefined' ? window : this, function (ResizeSensor) {

    /**
     *
     * @type {Function}
     * @constructor
     */
    var ElementQueries = function() {

        var trackingActive = false;
        var elements = [];

        /**
         *
         * @param element
         * @returns {Number}
         */
        function getEmSize(element) {
            if (!element) {
                element = document.documentElement;
            }
            var fontSize = window.getComputedStyle(element, null).fontSize;
            return parseFloat(fontSize) || 16;
        }

        /**
         *
         * @copyright https://github.com/Mr0grog/element-query/blob/master/LICENSE
         *
         * @param {HTMLElement} element
         * @param {*} value
         * @returns {*}
         */
        function convertToPx(element, value) {
            var numbers = value.split(/\d/);
            var units = numbers[numbers.length-1];
            value = parseFloat(value);
            switch (units) {
                case "px":
                    return value;
                case "em":
                    return value * getEmSize(element);
                case "rem":
                    return value * getEmSize();
                // Viewport units!
                // According to http://quirksmode.org/mobile/tableViewport.html
                // documentElement.clientWidth/Height gets us the most reliable info
                case "vw":
                    return value * document.documentElement.clientWidth / 100;
                case "vh":
                    return value * document.documentElement.clientHeight / 100;
                case "vmin":
                case "vmax":
                    var vw = document.documentElement.clientWidth / 100;
                    var vh = document.documentElement.clientHeight / 100;
                    var chooser = Math[units === "vmin" ? "min" : "max"];
                    return value * chooser(vw, vh);
                default:
                    return value;
                // for now, not supporting physical units (since they are just a set number of px)
                // or ex/ch (getting accurate measurements is hard)
            }
        }

        /**
         *
         * @param {HTMLElement} element
         * @constructor
         */
        function SetupInformation(element) {
            this.element = element;
            this.options = {};
            var key, option, width = 0, height = 0, value, actualValue, attrValues, attrValue, attrName;

            /**
             * @param {Object} option {mode: 'min|max', property: 'width|height', value: '123px'}
             */
            this.addOption = function(option) {
                var idx = [option.mode, option.property, option.value].join(',');
                this.options[idx] = option;
            };

            var attributes = ['min-width', 'min-height', 'max-width', 'max-height'];

            /**
             * Extracts the computed width/height and sets to min/max- attribute.
             */
            this.call = function() {
                // extract current dimensions
                width = this.element.offsetWidth;
                height = this.element.offsetHeight;

                attrValues = {};

                for (key in this.options) {
                    if (!this.options.hasOwnProperty(key)){
                        continue;
                    }
                    option = this.options[key];

                    value = convertToPx(this.element, option.value);

                    actualValue = option.property == 'width' ? width : height;
                    attrName = option.mode + '-' + option.property;
                    attrValue = '';

                    if (option.mode == 'min' && actualValue >= value) {
                        attrValue += option.value;
                    }

                    if (option.mode == 'max' && actualValue <= value) {
                        attrValue += option.value;
                    }

                    if (!attrValues[attrName]) attrValues[attrName] = '';
                    if (attrValue && -1 === (' '+attrValues[attrName]+' ').indexOf(' ' + attrValue + ' ')) {
                        attrValues[attrName] += ' ' + attrValue;
                    }
                }

                for (var k in attributes) {
                    if(!attributes.hasOwnProperty(k)) continue;

                    if (attrValues[attributes[k]]) {
                        this.element.setAttribute(attributes[k], attrValues[attributes[k]].substr(1));
                    } else {
                        this.element.removeAttribute(attributes[k]);
                    }
                }
            };
        }

        /**
         * @param {HTMLElement} element
         * @param {Object}      options
         */
        function setupElement(element, options) {
            if (element.elementQueriesSetupInformation) {
                element.elementQueriesSetupInformation.addOption(options);
            } else {
                element.elementQueriesSetupInformation = new SetupInformation(element);
                element.elementQueriesSetupInformation.addOption(options);
                element.elementQueriesSensor = new ResizeSensor(element, function() {
                    element.elementQueriesSetupInformation.call();
                });
            }
            element.elementQueriesSetupInformation.call();

            if (trackingActive && elements.indexOf(element) < 0) {
                elements.push(element);
            }
        }

        /**
         * @param {String} selector
         * @param {String} mode min|max
         * @param {String} property width|height
         * @param {String} value
         */
        var allQueries = {};
        function queueQuery(selector, mode, property, value) {
            if (typeof(allQueries[mode]) == 'undefined') allQueries[mode] = {};
            if (typeof(allQueries[mode][property]) == 'undefined') allQueries[mode][property] = {};
            if (typeof(allQueries[mode][property][value]) == 'undefined') allQueries[mode][property][value] = selector;
            else allQueries[mode][property][value] += ','+selector;
        }

        function getQuery(container) {
            var query;
            if (document.querySelectorAll) query = (container) ? container.querySelectorAll.bind(container) : document.querySelectorAll.bind(document);
            if (!query && 'undefined' !== typeof $$) query = $$;
            if (!query && 'undefined' !== typeof jQuery) query = jQuery;

            if (!query) {
                throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.';
            }

            return query;
        }

        /**
         * Start the magic. Go through all collected rules (readRules()) and attach the resize-listener.
         */
        function findElementQueriesElements(container) {
            var query = getQuery(container);

            for (var mode in allQueries) if (allQueries.hasOwnProperty(mode)) {

                for (var property in allQueries[mode]) if (allQueries[mode].hasOwnProperty(property)) {
                    for (var value in allQueries[mode][property]) if (allQueries[mode][property].hasOwnProperty(value)) {
                        var elements = query(allQueries[mode][property][value], container);
                        for (var i = 0, j = elements.length; i < j; i++) {
                            setupElement(elements[i], {
                                mode: mode,
                                property: property,
                                value: value
                            });
                        }
                    }
                }

            }
        }

        /**
         *
         * @param {HTMLElement} element
         */
        function attachResponsiveImage(element) {
            var children = [];
            var rules = [];
            var sources = [];
            var defaultImageId = 0;
            var lastActiveImage = -1;
            var loadedImages = [];

            for (var i in element.children) {
                if(!element.children.hasOwnProperty(i)) continue;

                if (element.children[i].tagName && element.children[i].tagName.toLowerCase() === 'img') {
                    children.push(element.children[i]);

                    var minWidth = element.children[i].getAttribute('min-width') || element.children[i].getAttribute('data-min-width');
                    //var minHeight = element.children[i].getAttribute('min-height') || element.children[i].getAttribute('data-min-height');
                    var src = element.children[i].getAttribute('data-src') || element.children[i].getAttribute('url');

                    sources.push(src);

                    var rule = {
                        minWidth: minWidth
                    };

                    rules.push(rule);

                    if (!minWidth) {
                        defaultImageId = children.length - 1;
                        element.children[i].style.display = 'block';
                    } else {
                        element.children[i].style.display = 'none';
                    }
                }
            }

            lastActiveImage = defaultImageId;

            function check() {
                var imageToDisplay = false, i;

                for (i in children){
                    if(!children.hasOwnProperty(i)) continue;

                    if (rules[i].minWidth) {
                        if (element.offsetWidth > rules[i].minWidth) {
                            imageToDisplay = i;
                        }
                    }
                }

                if (!imageToDisplay) {
                    //no rule matched, show default
                    imageToDisplay = defaultImageId;
                }

                if (lastActiveImage != imageToDisplay) {
                    //image change

                    if (!loadedImages[imageToDisplay]){
                        //image has not been loaded yet, we need to load the image first in memory to prevent flash of
                        //no content

                        var image = new Image();
                        image.onload = function() {
                            children[imageToDisplay].src = sources[imageToDisplay];

                            children[lastActiveImage].style.display = 'none';
                            children[imageToDisplay].style.display = 'block';

                            loadedImages[imageToDisplay] = true;

                            lastActiveImage = imageToDisplay;
                        };

                        image.src = sources[imageToDisplay];
                    } else {
                        children[lastActiveImage].style.display = 'none';
                        children[imageToDisplay].style.display = 'block';
                        lastActiveImage = imageToDisplay;
                    }
                } else {
                    //make sure for initial check call the .src is set correctly
                    children[imageToDisplay].src = sources[imageToDisplay];
                }
            }

            element.resizeSensor = new ResizeSensor(element, check);
            check();

            if (trackingActive) {
                elements.push(element);
            }
        }

        function findResponsiveImages(){
            var query = getQuery();

            var elements = query('[data-responsive-image],[responsive-image]');
            for (var i = 0, j = elements.length; i < j; i++) {
                attachResponsiveImage(elements[i]);
            }
        }

        var regex = /,?[\s\t]*([^,\n]*?)((?:\[[\s\t]*?(?:min|max)-(?:width|height)[\s\t]*?[~$\^]?=[\s\t]*?"[^"]*?"[\s\t]*?])+)([^,\n\s\{]*)/mgi;
        var attrRegex = /\[[\s\t]*?(min|max)-(width|height)[\s\t]*?[~$\^]?=[\s\t]*?"([^"]*?)"[\s\t]*?]/mgi;
        /**
         * @param {String} css
         */
        function extractQuery(css) {
            var match, smatch, attrs, attrMatch;
            
            css = css.replace(/'/g, '"');
            while (null !== (match = regex.exec(css))) {
                smatch = match[1] + match[3];
                attrs = match[2];

                while (null !== (attrMatch = attrRegex.exec(attrs))) {
                    queueQuery(smatch, attrMatch[1], attrMatch[2], attrMatch[3]);
                }
            }
        }

        /**
         * @param {CssRule[]|String} rules
         */
        function readRules(rules) {
            var selector = '';
            if (!rules) {
                return;
            }
            if ('string' === typeof rules) {
                rules = rules.toLowerCase();
                if (-1 !== rules.indexOf('min-width') || -1 !== rules.indexOf('max-width')) {
                    extractQuery(rules);
                }
            } else {
                for (var i = 0, j = rules.length; i < j; i++) {
                    if (1 === rules[i].type) {
                        selector = rules[i].selectorText || rules[i].cssText;
                        if (-1 !== selector.indexOf('min-height') || -1 !== selector.indexOf('max-height')) {
                            extractQuery(selector);
                        }else if(-1 !== selector.indexOf('min-width') || -1 !== selector.indexOf('max-width')) {
                            extractQuery(selector);
                        }
                    } else if (4 === rules[i].type) {
                        readRules(rules[i].cssRules || rules[i].rules);
                    } else if (3 === rules[i].type) {
                        readRules(rules[i].styleSheet.cssRules);
                    }
                }
            }
        }

        var defaultCssInjected = false;

        /**
         * Searches all css rules and setups the event listener to all elements with element query rules..
         *
         * @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements
         *                               (no garbage collection possible if you don not call .detach() first)
         */
        this.init = function(withTracking) {
            trackingActive = typeof withTracking === 'undefined' ? false : withTracking;

            for (var i = 0, j = document.styleSheets.length; i < j; i++) {
                try {
                    readRules(document.styleSheets[i].cssRules || document.styleSheets[i].rules || document.styleSheets[i].cssText);
                } catch(e) {
                    if (e.name !== 'SecurityError' && e.name !== 'InvalidAccessError') {
                        throw e;
                    }
                }
            }

            if (!defaultCssInjected) {
                var style = document.createElement('style');
                style.type = 'text/css';
                style.innerHTML = '[responsive-image] > img, [data-responsive-image] {overflow: hidden; padding: 0; } [responsive-image] > img, [data-responsive-image] > img { width: 100%;}';
                document.getElementsByTagName('head')[0].appendChild(style);
                defaultCssInjected = true;
            }

            findElementQueriesElements();
            findResponsiveImages();
        };

        /**
         * Go through all collected rules (readRules()) and attach the resize-listener.
         *
         * @param {HTMLElement} container only elements of the container are considered (document.body if not set)
         */
        this.findElementQueriesElements = function(container) {
            findElementQueriesElements(container);
        };

        /**
         *
         * @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements
         *                               (no garbage collection possible if you don not call .detach() first)
         */
        this.update = function(withTracking) {
            this.init(withTracking);
        };

        this.detach = function() {
            if (!trackingActive) {
                throw 'withTracking is not enabled. We can not detach elements since we don not store it.' +
                'Use ElementQueries.withTracking = true; before domready or call ElementQueryes.update(true).';
            }

            var element;
            while (element = elements.pop()) {
                ElementQueries.detach(element);
            }

            elements = [];
        };
    };

    /**
     *
     * @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements
     *                               (no garbage collection possible if you don not call .detach() first)
     */
    ElementQueries.update = function(withTracking) {
        ElementQueries.instance.update(withTracking);
    };

    /**
     * Removes all sensor and elementquery information from the element.
     *
     * @param {HTMLElement} element
     */
    ElementQueries.detach = function(element) {
        if (element.elementQueriesSetupInformation) {
            //element queries
            element.elementQueriesSensor.detach();
            delete element.elementQueriesSetupInformation;
            delete element.elementQueriesSensor;

        } else if (element.resizeSensor) {
            //responsive image

            element.resizeSensor.detach();
            delete element.resizeSensor;
        } else {
            //console.log('detached already', element);
        }
    };

    ElementQueries.withTracking = false;

    ElementQueries.init = function() {
        if (!ElementQueries.instance) {
            ElementQueries.instance = new ElementQueries();
        }

        ElementQueries.instance.init(ElementQueries.withTracking);
    };

    var domLoaded = function (callback) {
        /* Internet Explorer */
        /*@cc_on
         @if (@_win32 || @_win64)
         document.write('<script id="ieScriptLoad" defer src="//:"><\/script>');
         document.getElementById('ieScriptLoad').onreadystatechange = function() {
         if (this.readyState == 'complete') {
         callback();
         }
         };
         @end @*/
        /* Mozilla, Chrome, Opera */
        if (document.addEventListener) {
            document.addEventListener('DOMContentLoaded', callback, false);
        }
        /* Safari, iCab, Konqueror */
        else if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
            var DOMLoadTimer = setInterval(function () {
                if (/loaded|complete/i.test(document.readyState)) {
                    callback();
                    clearInterval(DOMLoadTimer);
                }
            }, 10);
        }
        /* Other web browsers */
        else window.onload = callback;
    };

    ElementQueries.findElementQueriesElements = function(container) {
        ElementQueries.instance.findElementQueriesElements(container);
    };

    ElementQueries.listen = function() {
        domLoaded(ElementQueries.init);
    };

    return ElementQueries;

}));

!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AOS=t():e.AOS=t()}(this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return e[o].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var n={};return t.m=e,t.c=n,t.p="dist/",t(0)}([function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},r=n(1),a=(o(r),n(6)),u=o(a),c=n(7),f=o(c),s=n(8),d=o(s),l=n(9),p=o(l),m=n(10),b=o(m),v=n(11),y=o(v),g=n(14),h=o(g),w=[],k=!1,x={offset:120,delay:0,easing:"ease",duration:400,disable:!1,once:!1,startEvent:"DOMContentLoaded",throttleDelay:99,debounceDelay:50,disableMutationObserver:!1},j=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(e&&(k=!0),k)return w=(0,y.default)(w,x),(0,b.default)(w,x.once),w},O=function(){w=(0,h.default)(),j()},_=function(){w.forEach(function(e,t){e.node.removeAttribute("data-aos"),e.node.removeAttribute("data-aos-easing"),e.node.removeAttribute("data-aos-duration"),e.node.removeAttribute("data-aos-delay")})},S=function(e){return e===!0||"mobile"===e&&p.default.mobile()||"phone"===e&&p.default.phone()||"tablet"===e&&p.default.tablet()||"function"==typeof e&&e()===!0},z=function(e){x=i(x,e),w=(0,h.default)();var t=document.all&&!window.atob;return S(x.disable)||t?_():(document.querySelector("body").setAttribute("data-aos-easing",x.easing),document.querySelector("body").setAttribute("data-aos-duration",x.duration),document.querySelector("body").setAttribute("data-aos-delay",x.delay),"DOMContentLoaded"===x.startEvent&&["complete","interactive"].indexOf(document.readyState)>-1?j(!0):"load"===x.startEvent?window.addEventListener(x.startEvent,function(){j(!0)}):document.addEventListener(x.startEvent,function(){j(!0)}),window.addEventListener("resize",(0,f.default)(j,x.debounceDelay,!0)),window.addEventListener("orientationchange",(0,f.default)(j,x.debounceDelay,!0)),window.addEventListener("scroll",(0,u.default)(function(){(0,b.default)(w,x.once)},x.throttleDelay)),x.disableMutationObserver||(0,d.default)("[data-aos]",O),w)};e.exports={init:z,refresh:j,refreshHard:O}},function(e,t){},,,,,function(e,t){(function(t){"use strict";function n(e,t,n){function o(t){var n=b,o=v;return b=v=void 0,k=t,g=e.apply(o,n)}function r(e){return k=e,h=setTimeout(s,t),_?o(e):g}function a(e){var n=e-w,o=e-k,i=t-n;return S?j(i,y-o):i}function c(e){var n=e-w,o=e-k;return void 0===w||n>=t||n<0||S&&o>=y}function s(){var e=O();return c(e)?d(e):void(h=setTimeout(s,a(e)))}function d(e){return h=void 0,z&&b?o(e):(b=v=void 0,g)}function l(){void 0!==h&&clearTimeout(h),k=0,b=w=v=h=void 0}function p(){return void 0===h?g:d(O())}function m(){var e=O(),n=c(e);if(b=arguments,v=this,w=e,n){if(void 0===h)return r(w);if(S)return h=setTimeout(s,t),o(w)}return void 0===h&&(h=setTimeout(s,t)),g}var b,v,y,g,h,w,k=0,_=!1,S=!1,z=!0;if("function"!=typeof e)throw new TypeError(f);return t=u(t)||0,i(n)&&(_=!!n.leading,S="maxWait"in n,y=S?x(u(n.maxWait)||0,t):y,z="trailing"in n?!!n.trailing:z),m.cancel=l,m.flush=p,m}function o(e,t,o){var r=!0,a=!0;if("function"!=typeof e)throw new TypeError(f);return i(o)&&(r="leading"in o?!!o.leading:r,a="trailing"in o?!!o.trailing:a),n(e,t,{leading:r,maxWait:t,trailing:a})}function i(e){var t="undefined"==typeof e?"undefined":c(e);return!!e&&("object"==t||"function"==t)}function r(e){return!!e&&"object"==("undefined"==typeof e?"undefined":c(e))}function a(e){return"symbol"==("undefined"==typeof e?"undefined":c(e))||r(e)&&k.call(e)==d}function u(e){if("number"==typeof e)return e;if(a(e))return s;if(i(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=i(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(l,"");var n=m.test(e);return n||b.test(e)?v(e.slice(2),n?2:8):p.test(e)?s:+e}var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f="Expected a function",s=NaN,d="[object Symbol]",l=/^\s+|\s+$/g,p=/^[-+]0x[0-9a-f]+$/i,m=/^0b[01]+$/i,b=/^0o[0-7]+$/i,v=parseInt,y="object"==("undefined"==typeof t?"undefined":c(t))&&t&&t.Object===Object&&t,g="object"==("undefined"==typeof self?"undefined":c(self))&&self&&self.Object===Object&&self,h=y||g||Function("return this")(),w=Object.prototype,k=w.toString,x=Math.max,j=Math.min,O=function(){return h.Date.now()};e.exports=o}).call(t,function(){return this}())},function(e,t){(function(t){"use strict";function n(e,t,n){function i(t){var n=b,o=v;return b=v=void 0,O=t,g=e.apply(o,n)}function r(e){return O=e,h=setTimeout(s,t),_?i(e):g}function u(e){var n=e-w,o=e-O,i=t-n;return S?x(i,y-o):i}function f(e){var n=e-w,o=e-O;return void 0===w||n>=t||n<0||S&&o>=y}function s(){var e=j();return f(e)?d(e):void(h=setTimeout(s,u(e)))}function d(e){return h=void 0,z&&b?i(e):(b=v=void 0,g)}function l(){void 0!==h&&clearTimeout(h),O=0,b=w=v=h=void 0}function p(){return void 0===h?g:d(j())}function m(){var e=j(),n=f(e);if(b=arguments,v=this,w=e,n){if(void 0===h)return r(w);if(S)return h=setTimeout(s,t),i(w)}return void 0===h&&(h=setTimeout(s,t)),g}var b,v,y,g,h,w,O=0,_=!1,S=!1,z=!0;if("function"!=typeof e)throw new TypeError(c);return t=a(t)||0,o(n)&&(_=!!n.leading,S="maxWait"in n,y=S?k(a(n.maxWait)||0,t):y,z="trailing"in n?!!n.trailing:z),m.cancel=l,m.flush=p,m}function o(e){var t="undefined"==typeof e?"undefined":u(e);return!!e&&("object"==t||"function"==t)}function i(e){return!!e&&"object"==("undefined"==typeof e?"undefined":u(e))}function r(e){return"symbol"==("undefined"==typeof e?"undefined":u(e))||i(e)&&w.call(e)==s}function a(e){if("number"==typeof e)return e;if(r(e))return f;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(d,"");var n=p.test(e);return n||m.test(e)?b(e.slice(2),n?2:8):l.test(e)?f:+e}var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c="Expected a function",f=NaN,s="[object Symbol]",d=/^\s+|\s+$/g,l=/^[-+]0x[0-9a-f]+$/i,p=/^0b[01]+$/i,m=/^0o[0-7]+$/i,b=parseInt,v="object"==("undefined"==typeof t?"undefined":u(t))&&t&&t.Object===Object&&t,y="object"==("undefined"==typeof self?"undefined":u(self))&&self&&self.Object===Object&&self,g=v||y||Function("return this")(),h=Object.prototype,w=h.toString,k=Math.max,x=Math.min,j=function(){return g.Date.now()};e.exports=n}).call(t,function(){return this}())},function(e,t){"use strict";function n(e,t){var n=window.document,r=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,a=new r(o);i=t,a.observe(n.documentElement,{childList:!0,subtree:!0,removedNodes:!0})}function o(e){e&&e.forEach(function(e){var t=Array.prototype.slice.call(e.addedNodes),n=Array.prototype.slice.call(e.removedNodes),o=t.concat(n).filter(function(e){return e.hasAttribute&&e.hasAttribute("data-aos")}).length;o&&i()})}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){};t.default=n},function(e,t){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(){return navigator.userAgent||navigator.vendor||window.opera||""}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n<t.length;n++){var o=t[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,n,o){return n&&e(t.prototype,n),o&&e(t,o),t}}(),r=/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i,a=/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i,u=/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i,c=/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i,f=function(){function e(){n(this,e)}return i(e,[{key:"phone",value:function(){var e=o();return!(!r.test(e)&&!a.test(e.substr(0,4)))}},{key:"mobile",value:function(){var e=o();return!(!u.test(e)&&!c.test(e.substr(0,4)))}},{key:"tablet",value:function(){return this.mobile()&&!this.phone()}}]),e}();t.default=new f},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e,t,n){var o=e.node.getAttribute("data-aos-once");t>e.position?e.node.classList.add("aos-animate"):"undefined"!=typeof o&&("false"===o||!n&&"true"!==o)&&e.node.classList.remove("aos-animate")},o=function(e,t){var o=window.pageYOffset,i=window.innerHeight;e.forEach(function(e,r){n(e,i+o,t)})};t.default=o},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(12),r=o(i),a=function(e,t){return e.forEach(function(e,n){e.node.classList.add("aos-init"),e.position=(0,r.default)(e.node,t.offset)}),e};t.default=a},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(13),r=o(i),a=function(e,t){var n=0,o=0,i=window.innerHeight,a={offset:e.getAttribute("data-aos-offset"),anchor:e.getAttribute("data-aos-anchor"),anchorPlacement:e.getAttribute("data-aos-anchor-placement")};switch(a.offset&&!isNaN(a.offset)&&(o=parseInt(a.offset)),a.anchor&&document.querySelectorAll(a.anchor)&&(e=document.querySelectorAll(a.anchor)[0]),n=(0,r.default)(e).top,a.anchorPlacement){case"top-bottom":break;case"center-bottom":n+=e.offsetHeight/2;break;case"bottom-bottom":n+=e.offsetHeight;break;case"top-center":n+=i/2;break;case"bottom-center":n+=i/2+e.offsetHeight;break;case"center-center":n+=i/2+e.offsetHeight/2;break;case"top-top":n+=i;break;case"bottom-top":n+=e.offsetHeight+i;break;case"center-top":n+=e.offsetHeight/2+i}return a.anchorPlacement||a.offset||isNaN(t)||(o=t),n+o};t.default=a},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){for(var t=0,n=0;e&&!isNaN(e.offsetLeft)&&!isNaN(e.offsetTop);)t+=e.offsetLeft-("BODY"!=e.tagName?e.scrollLeft:0),n+=e.offsetTop-("BODY"!=e.tagName?e.scrollTop:0),e=e.offsetParent;return{top:n,left:t}};t.default=n},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){return e=e||document.querySelectorAll("[data-aos]"),Array.prototype.map.call(e,function(e){return{node:e}})};t.default=n}])});
/*!
 * parallax.js v1.5.0 (http://pixelcog.github.io/parallax.js/)
 * @copyright 2016 PixelCog, Inc.
 * @license MIT (https://github.com/pixelcog/parallax.js/blob/master/LICENSE)
 */
!function(t,i,e,s){function o(i,e){var r=this;"object"==typeof e&&(delete e.refresh,delete e.render,t.extend(this,e)),this.$element=t(i),!this.imageSrc&&this.$element.is("img")&&(this.imageSrc=this.$element.attr("src"));var h=(this.position+"").toLowerCase().match(/\S+/g)||[];if(h.length<1&&h.push("center"),1==h.length&&h.push(h[0]),"top"!=h[0]&&"bottom"!=h[0]&&"left"!=h[1]&&"right"!=h[1]||(h=[h[1],h[0]]),this.positionX!==s&&(h[0]=this.positionX.toLowerCase()),this.positionY!==s&&(h[1]=this.positionY.toLowerCase()),r.positionX=h[0],r.positionY=h[1],"left"!=this.positionX&&"right"!=this.positionX&&(isNaN(parseInt(this.positionX))?this.positionX="center":this.positionX=parseInt(this.positionX)),"top"!=this.positionY&&"bottom"!=this.positionY&&(isNaN(parseInt(this.positionY))?this.positionY="center":this.positionY=parseInt(this.positionY)),this.position=this.positionX+(isNaN(this.positionX)?"":"px")+" "+this.positionY+(isNaN(this.positionY)?"":"px"),navigator.userAgent.match(/(iPod|iPhone|iPad)/)&&this.iosDisabled)return this.imageSrc&&this.iosFix&&!this.$element.is("img")&&this.$element.css({backgroundImage:'url("'+this.imageSrc+'")',backgroundSize:"cover",backgroundPosition:this.position}),this;if(navigator.userAgent.match(/(Android)/)&&this.androidDisabled)return this.imageSrc&&this.androidFix&&!this.$element.is("img")&&this.$element.css({backgroundImage:'url("'+this.imageSrc+'")',backgroundSize:"cover",backgroundPosition:this.position}),this;this.$mirror=t("<div />").prependTo(this.mirrorContainer);var a=this.$element.find(">.parallax-slider"),n=!1;0==a.length?this.$slider=t("<img />").prependTo(this.$mirror):(this.$slider=a.prependTo(this.$mirror),n=!0),this.$mirror.addClass("parallax-mirror").css({visibility:"hidden",zIndex:this.zIndex,position:"fixed",top:0,left:0,overflow:"hidden"}),this.$slider.addClass("parallax-slider").one("load",function(){r.naturalHeight&&r.naturalWidth||(r.naturalHeight=this.naturalHeight||this.height||1,r.naturalWidth=this.naturalWidth||this.width||1),r.aspectRatio=r.naturalWidth/r.naturalHeight,o.isSetup||o.setup(),o.sliders.push(r),o.isFresh=!1,o.requestRender()}),n||(this.$slider[0].src=this.imageSrc),(this.naturalHeight&&this.naturalWidth||this.$slider[0].complete||a.length>0)&&this.$slider.trigger("load")}!function(){for(var t=0,e=["ms","moz","webkit","o"],s=0;s<e.length&&!i.requestAnimationFrame;++s)i.requestAnimationFrame=i[e[s]+"RequestAnimationFrame"],i.cancelAnimationFrame=i[e[s]+"CancelAnimationFrame"]||i[e[s]+"CancelRequestAnimationFrame"];i.requestAnimationFrame||(i.requestAnimationFrame=function(e){var s=(new Date).getTime(),o=Math.max(0,16-(s-t)),r=i.setTimeout(function(){e(s+o)},o);return t=s+o,r}),i.cancelAnimationFrame||(i.cancelAnimationFrame=function(t){clearTimeout(t)})}();var r=!1;try{var h=Object.defineProperty({},"passive",{get:function(){r=!0}});i.addEventListener("test",null,h)}catch(t){}t.extend(o.prototype,{speed:.2,bleed:0,zIndex:-100,iosFix:!0,iosDisabled:!0,androidFix:!0,androidDisabled:!0,position:"center",overScrollFix:!1,mirrorContainer:"body",refresh:function(){this.boxWidth=this.$element.outerWidth(),this.boxHeight=this.$element.outerHeight()+2*this.bleed,this.boxOffsetTop=this.$element.offset().top-this.bleed,this.boxOffsetLeft=this.$element.offset().left,this.boxOffsetBottom=this.boxOffsetTop+this.boxHeight;var t,i=o.winHeight,e=o.docHeight,s=Math.min(this.boxOffsetTop,e-i),r=Math.max(this.boxOffsetTop+this.boxHeight-i,0),h=this.boxHeight+(s-r)*(1-this.speed)|0,a=(this.boxOffsetTop-s)*(1-this.speed)|0;h*this.aspectRatio>=this.boxWidth?(this.imageWidth=h*this.aspectRatio|0,this.imageHeight=h,this.offsetBaseTop=a,t=this.imageWidth-this.boxWidth,"left"==this.positionX?this.offsetLeft=0:"right"==this.positionX?this.offsetLeft=-t:isNaN(this.positionX)?this.offsetLeft=-t/2|0:this.offsetLeft=Math.max(this.positionX,-t)):(this.imageWidth=this.boxWidth,this.imageHeight=this.boxWidth/this.aspectRatio|0,this.offsetLeft=0,t=this.imageHeight-h,"top"==this.positionY?this.offsetBaseTop=a:"bottom"==this.positionY?this.offsetBaseTop=a-t:isNaN(this.positionY)?this.offsetBaseTop=a-t/2|0:this.offsetBaseTop=a+Math.max(this.positionY,-t))},render:function(){var t=o.scrollTop,i=o.scrollLeft,e=this.overScrollFix?o.overScroll:0,s=t+o.winHeight;this.boxOffsetBottom>t&&this.boxOffsetTop<=s?(this.visibility="visible",this.mirrorTop=this.boxOffsetTop-t,this.mirrorLeft=this.boxOffsetLeft-i,this.offsetTop=this.offsetBaseTop-this.mirrorTop*(1-this.speed)):this.visibility="hidden",this.$mirror.css({transform:"translate3d("+this.mirrorLeft+"px, "+(this.mirrorTop-e)+"px, 0px)",visibility:this.visibility,height:this.boxHeight,width:this.boxWidth}),this.$slider.css({transform:"translate3d("+this.offsetLeft+"px, "+this.offsetTop+"px, 0px)",position:"absolute",height:this.imageHeight,width:this.imageWidth,maxWidth:"none"})}}),t.extend(o,{scrollTop:0,scrollLeft:0,winHeight:0,winWidth:0,docHeight:1<<30,docWidth:1<<30,sliders:[],isReady:!1,isFresh:!1,isBusy:!1,setup:function(){function s(){if(f==i.pageYOffset)return i.requestAnimationFrame(s),!1;f=i.pageYOffset,h.render(),i.requestAnimationFrame(s)}if(!this.isReady){var h=this,a=t(e),n=t(i),l=function(){o.winHeight=n.height(),o.winWidth=n.width(),o.docHeight=a.height(),o.docWidth=a.width()},d=function(){var t=n.scrollTop(),i=o.docHeight-o.winHeight,e=o.docWidth-o.winWidth;o.scrollTop=Math.max(0,Math.min(i,t)),o.scrollLeft=Math.max(0,Math.min(e,n.scrollLeft())),o.overScroll=Math.max(t-i,Math.min(t,0))},p=this.scrollListener=function(){d(),o.requestRender()};n.on("resize.px.parallax load.px.parallax",function(){l(),h.refresh(),o.isFresh=!1,o.requestRender()}).on("scroll.px.parallax load.px.parallax",p),function(t,i,e){r?t.addEventListener(i,e,{passive:!0}):t.addEventListener(i,e)}(i,"touchmove",p),l(),d(),this.isReady=!0;var f=-1;s()}},configure:function(i){"object"==typeof i&&(delete i.refresh,delete i.render,t.extend(this.prototype,i))},refresh:function(){t.each(this.sliders,function(){this.refresh()}),this.isFresh=!0},render:function(){this.isFresh||this.refresh(),t.each(this.sliders,function(){this.render()})},requestRender:function(){this.render(),this.isBusy=!1},destroy:function(e){var s,r=t(e).data("px.parallax");for(r.$mirror.remove(),s=0;s<this.sliders.length;s+=1)this.sliders[s]==r&&this.sliders.splice(s,1);t(e).data("px.parallax",!1),0===this.sliders.length&&(t(i).off("scroll.px.parallax resize.px.parallax load.px.parallax"),function(t,i,e){t.removeEventListenr(i,e)}(i,"touchmove",this.scrollListener),this.isReady=!1,o.isSetup=!1)}});var a=t.fn.parallax;t.fn.parallax=function(s){return this.each(function(){var r=t(this),h="object"==typeof s&&s;this==i||this==e||r.is("body")?o.configure(h):r.data("px.parallax")?"object"==typeof s&&t.extend(r.data("px.parallax"),h):(h=t.extend({},r.data(),h),r.data("px.parallax",new o(this,h))),"string"==typeof s&&("destroy"==s?o.destroy(this):o[s]())})},t.fn.parallax.Constructor=o,t.fn.parallax.noConflict=function(){return t.fn.parallax=a,this},t(function(){t('[data-parallax="scroll"]').parallax()})}(jQuery,window,document);
$(function () {
    $('#gdpr-banner .refuse').on('click', function () {
        setConsentGDPR($(this), 0);
    });
    $('#gdpr-banner .accept').on('click', function () {
        setConsentGDPR($(this), 1);
    });
    $('#gdpr-banner-update').on('click', function () {
        setConsentGDPR($(this), -1);
    });
});

function setConsentGDPR(item, accept) {
    item.addClass('disabled-link');
    $.getJSON(ajaxURL + 'cms_consentGDPR', {
        accept: accept
    }, function (json) {
        if (json.error) {
            showError(json.data.message);
            item.removeClass('disabled-link');
        } else {
            showSuccess(json.data.message, true, {
                timer: 2500
            });
        }
    });
}

function displayBannerGDPR() {
    $('#gdpr-banner').slideDown('slow');
} // displayBannerGDPR()

function displayUpdateGDPR() {
    $('#gdpr-banner-update').appendTo($('footer.site-footer .copyright'));
    $('#gdpr-banner-update').slideDown('slow');
} // displayBannerGDPR()

function forceBannerGDPR() {
    $('body').prepend('<div id="gdpr-banner-backdrop" />');
    $('body').addClass('prevent-scroll');
}
function launchFrontendTutorial()
{
  var tour = new Tour({
    framework: 'bootstrap4',
    showProgressBar: false,
    showProgressText: true,
    localization: {
      buttonTexts: {
        prevButton: 'Précédent',
        nextButton: 'Suivant',
        pauseButton: 'Pause',
        resumeButton: 'Continuer',
        endTourButton: 'Arrêter'
      }
    },
    steps: [
      {
        showIfUnintendedOrphan: true,
        preventInteraction: true,
        title: 'Guide d\'utilisation',
        content: 'Bienvenue dans ce guide d\'utilisation. Nous allons vous montrer en quelques minutes les éléments essentiels pour administrer votre site.'
      },
      {
        element: '#frontEdit',
        preventInteraction: true,
        title: 'Menu d\'édition',
        content: 'Voici le menu d\'édition vous permettant de modifier et configurer votre site.<br>Via celui-ci, vous pouvez gérer les pages, le design et accéder au panneau d\'administration.',
        placement: 'bottom'
      },
      {
        element: '#lockEditmenu',
        preventInteraction: true,
        title: 'Activation de l\'éditeur',
        content: 'Ce bouton vous permet d\'activer ou de désactiver l\'éditeur de contenus. Lorsqu\'il est actif, il vous permet de modifier les pages en y ajoutant des modules.<br>Lorsque vous ne modifiez pas vos contenus, nous vous recommandons de le désactiver pour avoir le même rendu que vos visiteurs.',
        placement: 'bottom'
      },
      {
        element: '#edit-link-1',
        preventInteraction: true,
        title: 'Liste des pages',
        content: 'En cliquant sur ce bouton, vous aurez accès à la liste des pages de votre site et pourrez les modifier ou en créer de nouvelles.<br><br><img src="/assets/images/website/tutorial/liste-pages.jpg" alt="" />',
        placement: 'bottom',
        onNext: function (tour) {
          $('#edit-link-1').next('.submenu').show();
        }
      },
      {
        element: '#edit-link-4',
        preventInteraction: true,
        title: 'Ajouter une page',
        content: 'Pour ajouter une nouvelle page sur votre site, cliquez sur ce bouton. Vous n\'avez ensuite plus qu\'à remplir les informations demandées.',
        placement: 'bottom'
      },
      {
        element: '#edit-link-3',
        preventInteraction: true,
        title: 'Modifier la page courante',
        content: 'Ce bouton vous permet de modifier directement les informations de la page sur laquelle vous vous trouvez.',
        placement: 'bottom',
        onNext: function (tour) {
          $('#edit-link-1').next('.submenu').hide();
        }
      },
      {
        element: '#edit-link-5',
        preventInteraction: true,
        title: 'Apparence',
        content: 'Cette rubrique va vous permettre de personnaliser votre site : couleurs, polices de caractères, photos... vous y trouverez toutes les options pour rendre votre site unique et à votre image.',
        placement: 'bottom'
      },
      {
        element: '.section-new:first > a:first',
        preventInteraction: true,
        title: 'Créer une section',
        content: 'Les sections vous permettent de définir la mise en forme de vos pages et d\'ajouter des modules. La mise en forme est personnalisable : 1 colonne, 2 colonnes...<br><br><img src="/assets/images/website/tutorial/ajouter-section.jpg" alt="" />'
      },
      {
        element: '.section-add:first > .btn-new-module',
        preventInteraction: true,
        title: 'Ajouter un module',
        content: 'Dans chaque section vous aurez ce bouton vous permettant d\'y ajouter des modules. Les modules sont des contenus tel que des textes, photos ou fonctionnalités spécifiques.<br><br><img src="/assets/images/website/tutorial/ajouter-element.jpg" alt="" />',
      },
      {
        element: '.section-add:first > .btn-customize-column',
        preventInteraction: true,
        title: 'Personnaliser une colonne',
        content: 'Chaque colonne d\'une section peut être personnalisée. Cela vous permet de changer les espacements ou encore d\'ajouter une couleur ou photo en fond.',
      },
      {
        element: '.bloc-container:first',
        preventInteraction: true,
        title: 'Module',
        content: 'Voici un exemple de module. Pour chaque module, vous pouvez voir le menu d\'édition en passant la souris dessus.',
        onNext: function (tour) {
          // Display edit menu of first module
          $('.bloc-edit-menu').first().addClass('force-visible');
        }
      },
      {
        element: '.bloc-edit-menu:first',
        preventInteraction: true,
        title: 'Edition d\'un module',
        content: 'Voici le menu d\'édition : vous y trouverez les actions vous permettant de modifier, déplacer ou supprimer un module d\'une page.',
        placement: 'bottom'
      },
      {
        element: '.bloc-edit-menu:first a[data-action="update"]',
        preventInteraction: true,
        title: 'Modifier',
        content: 'Cliquez sur ce bouton pour modifier les informations de ce module.',
        placement: 'bottom'
      },
      {
        element: '.bloc-edit-menu:first a[data-action="copy"]',
        preventInteraction: true,
        title: 'Dupliquer',
        content: 'Ce bouton vous permet de faire une copie du module.',
        placement: 'bottom'
      },
      {
        element: '.bloc-edit-menu:first a[data-action="moveTop"]',
        preventInteraction: true,
        title: 'Déplacer dans la page',
        content: 'Les deux fleches vous permettent de déplacer le module à l\'intérieur de sa section.<br>Vous pouvez également déplacer un module via un Glisser-déposer.',
        placement: 'bottom'
      },
      {
        element: '.bloc-edit-menu:first a[data-action="updatePage"]',
        preventInteraction: true,
        title: 'Déplacer dans une autre page',
        content: 'Via ce bouton, vous pouvez déplacer ce module dans une autre page.',
        placement: 'bottom'
      },
      {
        element: '.bloc-edit-menu:first a[data-action="delete"]',
        preventInteraction: true,
        title: 'Supprimer',
        content: 'Cliquez sur ce bouton pour supprimer le module de la page.<br>En cas d\'erreur, vous pourrez retrouver votre module dans la corbeille en haut de la page.',
        placement: 'bottom',
        onNext: function (tour) {
          // Hide edit menu of first module
          $('.bloc-edit-menu').first().removeClass('force-visible');
        }
      },
      {
        element: '#blocsInTrash',
        preventInteraction: true,
        title: 'Corbeille',
        content: 'Voici la corbeille : vous y trouverez les modules que vous avez supprimé sur cette page et pourrez les restaurer ou les supprimer définitivement.',
        placement: 'bottom'
      },
      {
        element: '#backend',
        title: 'Accéder au panneau d\'administration',
        content: 'Félicitations, vous avez terminé ce guide. Vous pouvez à tout moment accéder à nouveau aux guides interactifs depuis le bouton "Aide" en haut à droite de l\'écran.',
        placement: 'bottom'
      },
    ],
    onStart: function(tour) {
      $('body').addClass('tutorial-active');
      $('html, body').animate({ scrollTop: 0 }, 'slow');
    },
    onEnd: function(tour) {
      $('body').removeClass('tutorial-active');
      saveTutorialState('frontend', -1);
    }
  });

  tour.restart();
} // launchFrontendTutorial()

function launchBackendTutorial()
{
  var tour = new Tour({
    framework: 'bootstrap4',
    showProgressBar: false,
    showProgressText: true,
    localization: {
      buttonTexts: {
        prevButton: 'Précédent',
        nextButton: 'Suivant',
        pauseButton: 'Pause',
        resumeButton: 'Continuer',
        endTourButton: 'Arrêter'
      }
    },
    steps: [
      {
        showIfUnintendedOrphan: true,
        preventInteraction: true,
        title: 'Guide d\'utilisation',
        content: 'Bienvenue sur le panneau d\'administration de votre site. C\'est via cette interface que vous allez pouvoir configurer votre site internet.'
      },
      {
        element: '#statistics',
        preventInteraction: true,
        title: 'Statistiques',
        content: 'Vous pourrez accéder ici aux statistiques de visite de votre site internet sur les 7 derniers jours.',
        placement: 'top'
      },
      {
        element: '#quicklinks',
        preventInteraction: true,
        title: 'Liens rapides',
        content: 'Voici des liens rapides pour accéder aux éléments principaux du panneau d\'administration.',
        placement: 'bottom'
      },
      {
        element: '#quicklink-configuration',
        preventInteraction: true,
        title: 'Configuration',
        content: 'Accédez à cette page pour modifier la configuration générale du site : activation, adresse e-mail etc...',
        placement: 'bottom'
      },
      {
        element: '#quicklink-design',
        preventInteraction: true,
        title: 'Design',
        content: 'Cette page vous permet de modifier l\'apparence de votre site internet.',
        placement: 'bottom'
      },
      {
        element: '#quicklink-pages',
        preventInteraction: true,
        title: 'Pages',
        content: 'Vous pouvez consulter la liste des pages de votre site via ce lien. Vous pourrez les modifier et en créer de nouvelles.',
        placement: 'bottom'
      },
      {
        element: '#quicklink-modules',
        preventInteraction: true,
        title: 'Modules',
        content: 'Les modules sont des éléments que vous ajoutez dans vos pages. Vous pouvez ici choisir ceux que vous souhaitez activer.',
        placement: 'bottom'
      },
      {
        element: '#quicklink-extensions',
        preventInteraction: true,
        title: 'Extensions',
        content: 'Besoin de rajouter plus de fonctionnalités à votre site ? Activez l\'une de nos extensions.',
        placement: 'bottom'
      },
      {
        element: '#quicklink-support',
        preventInteraction: true,
        title: 'Support',
        content: 'Si vous rencontrez des difficultés en administrant votre site, vous pouvez ouvrir des tickets de support et nous nous efforcerons de vous aider.',
        placement: 'bottom'
      },
      {
        element: '#sidebar',
        preventInteraction: true,
        title: 'Menu',
        content: 'Voici le menu du panneau d\'administration dans lequel vous trouverez toutes les options de configuration du site.<br>Nous allons passer en revue quelques-unes d\'entre elles.',
        placement: 'right',
        onNext: function (tour) {
          $('#menu-category-2 > a').click()
        }
      },
      {
        element: '#sidebar',
        preventInteraction: true,
        title: 'Explorateur de fichiers',
        content: 'L\'explorateur de fichiers vous permet de gérer les fichiers multimédia de votre site internet. En y accédant, vous pouvez voir vos fichiers et en ajouter de nouveaux.',
        placement: 'right'
      },
      {
        element: '#sidebar',
        preventInteraction: true,
        title: 'Formulaires de contact',
        content: 'Le lien "Formulaires de contact" vous permet de créer des formulaires de contact afin que vos visiteurs puissent vous contacter.',
        onNext: function (tour) {
          $('#menu-category-5 > a').click()
        }
      },
      {
        element: '#sidebar',
        preventInteraction: true,
        title: 'Domaines/E-mails',
        content: 'Afin d\'être visible, il est important d\'avoir un nom de domaine personnalisé. Vous pourrez via la page "Domaines et e-mails" acheter des domaines et activer des comptes e-mails',
        onNext: function (tour) {
          $('#menu-category-7 > a').click();
        }
      },
      {
        element: '#sidebar',
        preventInteraction: true,
        title: 'Langues',
        content: 'Besoin de traduire votre site ? Accédez à la rubrique "Langues" pour voir les langues disponibles et les activer.<br>Cela ne traduit pas automatiquement votre site : vous devez créer les traductions pour vos modules.'
      },
      {
        element: '#sidebar',
        preventInteraction: true,
        title: 'Menus',
        content: 'Via la page "Menus", vous pourrez créer des menus de navigation sur votre site et y ajouter des pages. Par défaut vous en avez un, le menu principal qui s\'affiche généralement en haut de votre site internet.',
        onNext: function (tour) {
          $('#menu-category-10 > a').click();
        }
      },
      {
        element: '#sidebar',
        preventInteraction: true,
        title: 'Contacts',
        content: 'Gérez les informations concernant le propriétaire du site et vos adresses de facturation pour le paiement des abonnements et options via la page "Contacts".'
      },
      {
        element: '#backToFrontend',
        preventInteraction: true,
        title: 'Guide terminé',
        content: 'Félicitations, ce guide d\'utilisation est terminé. Vous pouvez à tout moment accéder à nouveau aux guides interactifs depuis le bouton "Aide" en haut à droite de l\'écran.',
        position: 'bottom'
      }
    ],
    onStart: function(tour) {
      $('body').addClass('tutorial-active');
      $('html, body').animate({ scrollTop: 0 }, 'slow');
    },
    onEnd: function(tour) {
      $('body').removeClass('tutorial-active');
      saveTutorialState('backend', -1);
    }
  });

  tour.restart();
} // launchBackendTutorial()
/* ========================================================================
 *
 * Bootstrap Tourist v0.3.3-in progress
 * Copyright FFS 2019
 * @ IGreatlyDislikeJavascript on Github
 *
 * This code is a fork of bootstrap-tour, with a lot of extra features
 * and fixes. You can read about why this fork exists here:
 *
 * https://github.com/sorich87/bootstrap-tour/issues/713
 *
 * The entire purpose of this fork is to start rewriting bootstrap-tour
 * into native ES6 instead of the original coffeescript, and to implement
 * the features and fixes requested.
 *
 * I'm not a JS coder, so suggest you test very carefully and read the
 * docs before using.
 *
 * ========================================================================
 * ENTIRELY BASED UPON:
 *
 * bootstrap-tour
 * http://bootstraptour.com
 * Copyright 2012-2015 Ulrich Sossou
 *
 * ========================================================================
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/MIT
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================================
 */
(function (window, factory) {
	if (typeof define === 'function' && define.amd) {
		return define(['jquery'], function (jQuery) {
			return window.Tour = factory(jQuery);
		});
	} else if (typeof exports === 'object') {
		return module.exports = factory(require('jquery'));
	} else {
		return window.Tour = factory(window.jQuery);
	}
})(window, function ($) {

	const DOMID_BACKDROP = "#tourBackdrop";
	const DOMID_BACKDROP_TEMP = "#tourBackdrop-temp"; // used for @ibastevan zindex fix: https://github.com/IGreatlyDislikeJavascript/bootstrap-tourist/issues/38
	const DOMID_HIGHLIGHT = "#tourHighlight";
	const DOMID_HIGHLIGHT_TEMP = "#tourHighlight-temp"; // used for @ibastevan zindex fix: https://github.com/IGreatlyDislikeJavascript/bootstrap-tourist/issues/38
	const DOMID_PREVENT = "#tourPrevent";

	var Tour, document, objTemplates, objTemplatesButtonTexts;

	document = window.document;

	Tour = (function () {

		function Tour(options)
		{
			var storage;
			try
			{
				storage = window.localStorage;
			}
			catch (error)
			{
				storage = false;
			}

			// CUSTOMIZABLE TEXTS FOR BUTTONS
			// set defaults. We could of course add this to the $.extend({..localization: {} ...}) directly below.
			// However this is configured here, prior to the $.extend of options below, to enable a potential
			// future option of loading localization externally perhaps using $.getScript() etc.
			//
			// Note that these only affect the "default" templates (see objTemplates in this func below). The assumption is
			// that if user creates a tour with a custom template, they will name the buttons as required. We could force the
			// naming even in custom templates by identifying buttons in templates with data-role="...", but it seems more logical
			// NOT to do that...
			//
			// Finally, it's simple to allow different localization/button texts per tour step. To do this, alter the $.extend in
			// Tour.prototype.getStep() and subsequent code to load the per-step localization, identify the buttons by data-role, and
			// make the appropriate changes. That seems like a very niche requirement so it's not implemented here.
			objTemplatesButtonTexts =	{
											prevButton: "Prev",
											nextButton: "Next",
											pauseButton: "Pause",
											resumeButton: "Resume",
											endTourButton: "End Tour"
										};


			// GLOBAL OPTIONS take default options and overwrite with this tour options
			this._options = $.extend(true,
									{
										name: 'tour',
										steps: [],
										container: 'body',
										autoscroll: true,
										keyboard: true,
										storage: storage,
										debug: false,
										backdrop: false,
										backdropContainer: 'body',
										backdropOptions:	{
																highlightOpacity:			0.9,
																highlightColor:				"#FFF",
                                                                backdropSibling:            false,
																animation:	{
																				// can be string of css class or function signature: function(domElement, step) {}
																				backdropShow:			function(domElement, step)
																										{
																											domElement.fadeIn();
																										},
																				backdropHide:			function(domElement, step)
																										{
																											domElement.fadeOut("slow")
																										},
																				highlightShow:			function(domElement, step)
																										{
																											// calling step.fnPositionHighlight() is the same as:
																											// domElement.width($(step.element).outerWidth()).height($(step.element).outerHeight()).offset($(step.element).offset());
																											step.fnPositionHighlight();
																											domElement.fadeIn();
																										},
																				highlightTransition:	"tour-highlight-animation",
																				highlightHide:			function(domElement, step)
																										{
																											domElement.fadeOut("slow")
																										}
																			},
															},
										redirect: true,
										orphan: false,
										showIfUnintendedOrphan: false,
										duration: false,
										delay: false,
										basePath: '',
										template: null,
										localization:	{
															buttonTexts: objTemplatesButtonTexts
														},
										framework: 'bootstrap3',
										sanitizeWhitelist: [],
										sanitizeFunction: null,// function(content) return sanitizedContent
										showProgressBar: true,
										showProgressText: true,
										getProgressBarHTML: null,//function(percent) {},
										getProgressTextHTML: null,//function(stepNumber, percent, stepCount) {},
										afterSetState: function (key, value) {},
										afterGetState: function (key, value) {},
										afterRemoveState: function (key) {},
										onStart: function (tour) {},
										onEnd: function (tour) {},
										onShow: function (tour) {},
										onShown: function (tour) {},
										onHide: function (tour) {},
										onHidden: function (tour) {},
										onNext: function (tour) {},
										onPrev: function (tour) {},
										onPause: function (tour, duration) {},
										onResume: function (tour, duration) {},
										onRedirectError: function (tour) {},
										onElementUnavailable: null, // function (tour, stepNumber) {},
										onPreviouslyEnded: null, // function (tour) {},
										onModalHidden: null, // function(tour, stepNumber) {}
									}, options);

			if($(this._options.backdropContainer).length == 0)
			{
				this._options.backdropContainer = "body";
			}

			if(this._options.framework !== "bootstrap3" && this._options.framework !== "bootstrap4")
			{
				this._debug('Invalid framework specified: ' + this._options.framework);
				throw "Bootstrap Tourist: Invalid framework specified";
			}


			// create the templates

			// SEARCH PLACEHOLDER: TEMPLATES LOCATION
			objTemplates = {
							  bootstrap3	: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">&laquo; ' + this._options.localization.buttonTexts.prevButton + '</button> <button class="btn btn-sm btn-default" data-role="next">' + this._options.localization.buttonTexts.nextButton + ' &raquo;</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="' + this._options.localization.buttonTexts.pauseButton + '" data-resume-text="' + this._options.localization.buttonTexts.resumeButton + '">' + this._options.localization.buttonTexts.pauseButton + '</button> </div> <button class="btn btn-sm btn-default" data-role="end">' + this._options.localization.buttonTexts.endTourButton + '</button> </div> </div>',
							  bootstrap4	: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-header"></h3> <div class="popover-body"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-outline-secondary" data-role="prev">&laquo; ' + this._options.localization.buttonTexts.prevButton + '</button> <button class="btn btn-sm btn-outline-secondary" data-role="next">' + this._options.localization.buttonTexts.nextButton + ' &raquo;</button> <button class="btn btn-sm btn-outline-secondary" data-role="pause-resume" data-pause-text="' + this._options.localization.buttonTexts.pauseButton + '" data-resume-text="' + this._options.localization.buttonTexts.resumeButton + '">' + this._options.localization.buttonTexts.pauseButton + '</button> </div> <button class="btn btn-sm btn-outline-secondary" data-role="end">' + this._options.localization.buttonTexts.endTourButton + '</button> </div> </div>',
						  };

			// template option is default null. If not null after extend, caller has set a custom template, so don't touch it
			if(this._options.template === null)
			{
				// no custom template, so choose the template based on the framework
				if(objTemplates[this._options.framework] != null && objTemplates[this._options.framework] != undefined)
				{
					// there's a default template for the framework type specified in the options
					this._options.template = objTemplates[this._options.framework];

					this._debug('Using framework template: ' + this._options.framework);
				}
				else
				{
					this._debug('Warning: ' + this._options.framework + ' specified for template (no template option set), but framework is unknown. Tour will not work!');
				}
			}
			else
			{
				this._debug('Using custom template');
			}

			if(typeof(this._options.sanitizeFunction) == "function")
			{
				this._debug("Using custom sanitize function in place of bootstrap - security implications, be careful");
			}
			else
			{
				this._options.sanitizeFunction = null;

				this._debug("Extending Bootstrap sanitize options");

				// no custom function, add our own
				// bootstrap 3.4.1 has whitelist functionality that strips tags from title, content etc of popovers and tooltips. Need to
				// add buttons to the whitelist otherwise the navigation buttons will be stripped from the popover content.
				// See issue: https://github.com/sorich87/bootstrap-tour/issues/723#issuecomment-471107788
				//
				// ** UPDATE: BS3 and BS4 have the whitelist function. However:
				//		BS3 uses $.fn.popover.Constructor.DEFAULTS.whiteList
				//		BS4 uses $.fn.popover.Constructor.Default.whiteList
				//	Even better, the CDN version of BS4 doesn't seem to include a whitelist property at all, which utterly screwed the first attempt at implementing
				// this, making it seem like my fix was working when in fact it was utterly broken.
				var defaultWhiteList = [];

				if(this._options.framework == "bootstrap4" && $.fn.popover.Constructor.Default.whiteList !== undefined)
				{
					defaultWhiteList = $.fn.popover.Constructor.Default.whiteList;
				}

				if(this._options.framework == "bootstrap3" && $.fn.popover.Constructor.DEFAULTS.whiteList !== undefined)
				{
					defaultWhiteList = $.fn.popover.Constructor.DEFAULTS.whiteList;
				}

				var whiteListAdditions = {
											"button":	["data-role", "style"],
											"img":		["style"],
											"div":		["style"]
										};


				// whitelist is object with properties that are arrays. Need to merge "manually", as using $.extend with recursion will still overwrite the arrays . Try
				// var whiteList = $.extend(true, {}, defaultWhiteList, whiteListAdditions, this._options.sanitizeWhitelist);
				// and inspect the img property to see the issue - the default whitelist "src" (array elem 0) is overwritten with additions "style"

				// clone the default whitelist object first, otherwise we change the defaults for all of bootstrap!
				var whiteList = $.extend(true, {}, defaultWhiteList);

				// iterate the additions, and merge them into the defaults. We could just hammer them in manually but this is a little more expandable for the future
				$.each(whiteListAdditions,	function( index, value )
											{
												if(whiteList[index] == undefined)
												{
													whiteList[index] = [];
												}

												$.merge(whiteList[index], value);
											});

				// and now do the same with the user specified whitelist in tour options
				$.each(this._options.sanitizeWhitelist,	function( index, value )
														{
															if(whiteList[index] == undefined)
															{
																whiteList[index] = [];
															}

															$.merge(whiteList[index], value);
														});

				// save the merged whitelist back to the options, this is used by popover initialization when each step is shown
				this._options.sanitizeWhitelist = whiteList;
			}

			this._current = null;
			this.backdrops = [];

			return this;
		}

		Tour.prototype.addSteps = function (steps) {
			var j,
			len,
			step;
			for (j = 0, len = steps.length; j < len; j++) {
				step = steps[j];
				this.addStep(step);
			}
			return this;
		};

		Tour.prototype.addStep = function (step) {
			this._options.steps.push(step);
			return this;
		};

		Tour.prototype.getStepCount = function() {
			return this._options.steps.length;
		};

		Tour.prototype.getStep = function (i) {
			if (this._options.steps[i] != null) {

				if(typeof(this._options.steps[i].element) == "function")
				{
					this._options.steps[i].element = this._options.steps[i].element();
				}

				// PER STEP OPTIONS: take the global options then override with this step's options.
				this._options.steps[i] =  $.extend(true,
														{
															id: "step-" + i,
															path: '',
															host: '',
															placement: 'right',
															positioning:{
																			adjustRelative: null	// this does nothing at the moment
																		},
															title: '',
															content: '<p></p>',
															next: i === this._options.steps.length - 1 ? -1 : i + 1,
															prev: i - 1,
															animation: true,
															container: this._options.container,
															autoscroll: this._options.autoscroll,
															backdrop: this._options.backdrop,
															//backdropOptions: this._options.backdropOptions, << SEE BELOW
															redirect: this._options.redirect,
															preventInteraction: false,
															orphan: this._options.orphan,
															showIfUnintendedOrphan: this._options.showIfUnintendedOrphan,
															duration: this._options.duration,
															delay: this._options.delay,
															delayOnElement:	null,
															template: this._options.template,
															showProgressBar: this._options.showProgressBar,
															showProgressText: this._options.showProgressText,
															getProgressBarHTML: this._options.getProgressBarHTML,
															getProgressTextHTML: this._options.getProgressTextHTML,
															onShow: this._options.onShow,
															onShown: this._options.onShown,
															onHide: this._options.onHide,
															onHidden: this._options.onHidden,
															onNext: this._options.onNext,
															onPrev: this._options.onPrev,
															onPause: this._options.onPause,
															onResume: this._options.onResume,
															onRedirectError: this._options.onRedirectError,
															onElementUnavailable: this._options.onElementUnavailable,
															onModalHidden: this._options.onModalHidden,
															internalFlags:	{
																				elementModal: null,					// will store the jq modal object for a step
																				elementModalOriginal: null,			// will store the original step.element string in steps that use a modal
																				elementBootstrapSelectpicker: null	// will store jq bootstrap select picker object
																			}
														},
														this._options.steps[i]
													);

				// required so we don't overwrite the global options.
				this._options.steps[i].backdropOptions = $.extend(true, {}, this._options.backdropOptions, this._options.steps[i].backdropOptions);

				// safety to ensure consistent logic - reflex must == true if reflexOnly == true
				if(this._options.steps[i].reflexOnly == true)
				{
					this._options.steps[i].reflex = true;
				}

				return this._options.steps[i];
			}
		};

		// step flags are used to remember specific internal step data across a tour
		Tour.prototype._setStepFlag = function(stepNumber, flagName, value)
		{
			if(this._options.steps[stepNumber] != null)
			{
				this._options.steps[stepNumber].internalFlags[flagName] = value;
			}
		};

		Tour.prototype._getStepFlag = function(stepNumber, flagName)
		{
			if(this._options.steps[stepNumber] != null)
			{
				return this._options.steps[stepNumber].internalFlags[flagName];
			}
		};


		//=======================================================================================================================================
		// Initiate tour and movement between steps

		Tour.prototype.init = function ()
		{
			console.log('You should remove Tour.init() from your code. It\'s not required with Bootstrap Tourist');
		}

		Tour.prototype.start = function ()
		{
			// Test if this tour has previously ended, and start() was called
			if(this.ended())
			{
				if(this._options.onPreviouslyEnded != null && typeof(this._options.onPreviouslyEnded) == "function")
				{
					this._debug('Tour previously ended, exiting. Call tour.restart() to force restart. Firing onPreviouslyEnded()');
					this._options.onPreviouslyEnded(this);
				}
				else
				{
					this._debug('Tour previously ended, exiting. Call tour.restart() to force restart');
				}

				return this;
			}

			// Call setCurrentStep() without params to start the tour using whatever step is recorded in localstorage. If no step recorded, tour starts
			// from first step. This provides the "resume tour" functionality.
			// Tour restart() simply removes the step from local storage
			this.setCurrentStep();

			// Create the backdrop and highlight divs
            this._createOverlayElements();

			this._initMouseNavigation();
			this._initKeyboardNavigation();

			// BS3: resize event must destroy and recreate both popper and background to ensure correct positioning
			// BS4: resize must destroy and recreate background, but popper.js handles popper positioning.
			var _this = this;
			$(window).on("resize.tour-" + _this._options.name,	function()
																{
																	_this.reshowCurrentStep();
																}
						);


			// Note: this call is not required, but remains here in case any future forkers want to reinstate the code that moves a non-orphan popover
			// when window is scrolled. Note that simply uncommenting this will not reinstate the code - _showPopoverAndOverlay automatically detects
			// if the current step is visible and will not reshow it. Therefore, to fully reinstate the "redraw on scroll" code, uncomment this and
			// also add appropriate code (to move popover & overlay) to the end of showPopover()
//			this._onScroll((function (_this)
//							{
//								return function ()
//								{
//									return _this._showPopoverAndOverlay(_this._current);
//								};
//							}
//						));

			// start the tour - see if user provided onStart function, and if it returns a promise, obey that promise before calling showStep
			var promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
			this._callOnPromiseDone(promise, this.showStep, this._current);

			return this;
		};

		Tour.prototype.next = function () {
			var promise;
			promise = this.hideStep();
			return this._callOnPromiseDone(promise, this._showNextStep);
		};

		Tour.prototype.prev = function () {
			var promise;
			promise = this.hideStep();
			return this._callOnPromiseDone(promise, this._showPrevStep);
		};

		Tour.prototype.goTo = function (i) {
			var promise;
			this._debug("goTo step " + i);
			promise = this.hideStep();
			return this._callOnPromiseDone(promise, this.showStep, i);
		};

		Tour.prototype.end = function ()
		{
			this._debug("Tour.end() called");

			var endHelper,
			promise;

			endHelper = (function (_this) {
				return function (e) {
					$(document).off("click.tour-" + _this._options.name);
					$(document).off("keyup.tour-" + _this._options.name);
					$(window).off("resize.tour-" + _this._options.name);
					$(window).off("scroll.tour-" + _this._options.name);
					_this._setState('end', 'yes');
					_this._clearTimer();
					$(".tour-step-element-reflex").removeClass("tour-step-element-reflex");
					$(".tour-step-element-reflexOnly").removeClass("tour-step-element-reflexOnly");
                    _this._hideBackdrop();
					_this._destroyOverlayElements();

					if (_this._options.onEnd != null)
					{
						return _this._options.onEnd(_this);
					}
				};
			})(this);
			promise = this.hideStep();
			return this._callOnPromiseDone(promise, endHelper);
		};

		Tour.prototype.ended = function () {
			return this._getState('end') == 'yes';
		};

		Tour.prototype.restart = function ()
		{
			this._removeState('current_step');
			this._removeState('end');
			this._removeState('redirect_to');
			return this.start();
		};

		Tour.prototype.pause = function () {
			var step;
			step = this.getStep(this._current);
			if (!(step && step.duration)) {
				return this;
			}
			this._paused = true;
			this._duration -= new Date().getTime() - this._start;
			window.clearTimeout(this._timer);
			this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining).");
			if (step.onPause != null) {
				return step.onPause(this, this._duration);
			}
		};

		Tour.prototype.resume = function () {
			var step;
			step = this.getStep(this._current);
			if (!(step && step.duration)) {
				return this;
			}
			this._paused = false;
			this._start = new Date().getTime();
			this._duration = this._duration || step.duration;
			this._timer = window.setTimeout((	function (_this)
												{
													return	function ()
															{
																if (_this._isLast())
																{
																	return _this.end();
																}
																else
																{
																	return _this.next();
																}
												};
											})(this), this._duration);
			this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration);
			if ((step.onResume != null) && this._duration !== step.duration) {
				return step.onResume(this, this._duration);
			}
		};

		// fully closes and reopens the current step, triggering all callbacks etc
		Tour.prototype.reshowCurrentStep = function()
		{
			this._debug("Reshowing current step " + this.getCurrentStepIndex());
			var promise;
			promise = this.hideStep();
			return this._callOnPromiseDone(promise, this.showStep, this._current);
		};


		//=======================================================================================================================================


		// hides current step
		Tour.prototype.hideStep = function ()
		{
			var hideDelay,
			hideStepHelper,
			promise,
			step;

			step = this.getStep(this.getCurrentStepIndex());

			if (!step)
			{
				return;
			}

			this._clearTimer();
			promise = this._makePromise(step.onHide != null ? step.onHide(this, this.getCurrentStepIndex()) : void 0);

			hideStepHelper = (function (_this)
			{
				return function (e)
				{
					var $element;

					$element = $(step.element);
					if (!($element.data('bs.popover') || $element.data('popover')))
					{
						$element = $('body');
					}

					if(_this._options.framework == "bootstrap3")
					{
						$element.popover('destroy');
					}

					if(_this._options.framework == "bootstrap4")
					{
						$element.popover('dispose');
					}

					$element.removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + _this.getCurrentStepIndex() + "-element").removeData('bs.popover');

					if (step.reflex)
					{
						$element.removeClass('tour-step-element-reflex').off((_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name);
						$element.removeClass('tour-step-element-reflexOnly');
					}

					// now handled by updateOverlayElements
                    //_this._hideOverlayElements(step);
					_this._unfixBootstrapSelectPickerZindex(step);

					// If this step was pointed at a modal, revert changes to the step.element. See the notes in showStep for explanation
					var tmpModalOriginalElement = _this._getStepFlag(_this.getCurrentStepIndex(), "elementModalOriginal");
					if(tmpModalOriginalElement != null)
					{
						_this._setStepFlag(_this.getCurrentStepIndex(), "elementModalOriginal", null);
						step.element = tmpModalOriginalElement;
					}

					if (step.onHidden != null)
					{
						return step.onHidden(_this);
					}
				};
			})(this);

			hideDelay = step.delay.hide || step.delay;
			if ({}
				.toString.call(hideDelay) === '[object Number]' && hideDelay > 0) {
				this._debug("Wait " + hideDelay + " milliseconds to hide the step " + (this._current + 1));
				window.setTimeout((function (_this) {
						return function () {
							return _this._callOnPromiseDone(promise, hideStepHelper);
						};
					})(this), hideDelay);
			} else {
				this._callOnPromiseDone(promise, hideStepHelper);
			}
			return promise;
		};

		// loads all required step info and prepares to show
		Tour.prototype.showStep = function (i) {
			var path,
			promise,
			showDelay,
			showStepHelper,
			skipToPrevious,
			step,
			$element;


			if(this.ended())
			{
				// Note: see feature addition #12 and "onPreviouslyEnded" option to understand when this._options.onEnd is called vs this._options.onPreviouslyEnded()
				this._debug('Tour ended, showStep prevented.');
				if(this._options.onEnd != null)
				{
					this._options.onEnd(this);
				}

				return this;
			}

			step = this.getStep(i);
			if (!step) {
				return;
			}

			skipToPrevious = i < this._current;
			promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
			this.setCurrentStep(i);

			path = (function () {
				switch ({}
					.toString.call(step.path)) {
				case '[object Function]':
					return step.path();
				case '[object String]':
					return this._options.basePath + step.path;
				default:
					return step.path;
				}
			}).call(this);


			if (step.redirect && this._isRedirect(step.host, path, document.location)) {
				this._redirect(step, i, path);
				if (!this._isJustPathHashDifferent(step.host, path, document.location)) {
					return;
				}
			}

			// will be set to element <div class="modal"> if modal in use
			var $modalObject = null;

			// is element a modal?
			if(step.orphan === false && ($(step.element).hasClass("modal") || $(step.element).data('bs.modal')))
			{
				// element is exactly the modal div
				$modalObject = $(step.element);

				// This is a hack solution. Original Tour uses step.element in multiple places and converts to jquery object as needed. This func uses $element,
				// but multiple other funcs simply use $(step.element) instead - keeping the original string element id in the step data and using jquery as needed.
				// This creates problems with dialogs, especially BootStrap Dialog plugin - in code terms, the dialog is everything from <div class="modal-dialog">,
				// but the actual visible positioned part of the dialog is <div class="modal-dialog"><div class="modal-content">. The tour must attach the popover to
				// modal-content div, NOT the modal-dialog div. But most coders + dialog plugins put the id on the modal-dialog div.
				// So for display, we must adjust the step element to point at modal-content under the modal-dialog div. However if we change the step.element
				// permanently to the modal-content (by changing tour._options.steps), this won't work if the step is reshown (plugin destroys modal, meaning
				// the element jq object is no longer valid) and could potentially screw up other
				// parts of a tour that have dialogs. So instead we record the original element used for this step that involves modals, change the step.element
				// to the modal-content div, then set it back when the step is hidden again.
				//
				// This is ONLY done because it's too difficult to unpick all the original tour code that uses step.element directly.
				this._setStepFlag(this.getCurrentStepIndex(), "elementModalOriginal", step.element);

				// fix the tour element, the actual visible offset comes from modal > modal-dialog > modal-content and step.element is used to calc this offset & size
				step.element = $(step.element).find(".modal-content:first");
			}

			$element = $(step.element);

			// is element inside a modal? Find the parent modal
			if($modalObject === null && $element.parents(".modal:first").length)
			{
				// find the parent modal div
				$modalObject = $element.parents(".modal:first");
			}

			// Is this step a modal?
			if($modalObject && $modalObject.length > 0)
			{
				// Yes, set up the modal helper - called when the modal is hidden. This enables the onModalHidden tour option.
				this._debug("Modal identified, onModalHidden callback available");

				// store the modal element for other calls
				this._setStepFlag(i, "elementModal", $modalObject)

				// modal in use, add callback
				var funcModalHelper = 	function(_this, $_modalObject)
										{
											return function ()
											{
												_this._debug("Modal close triggered");

												if(typeof(step.onModalHidden) == "function")
												{
													// if step onModalHidden returns false, do nothing. returns int, move to the step specified.
													// Otherwise continue regular next/end functionality
													var rslt = step.onModalHidden(_this, i);

													if(rslt === false)
													{
														_this._debug("onModalHidden returned exactly false, tour step unchanged");
														return;
													}

													if(Number.isInteger(rslt))
													{
														_this._debug("onModalHidden returned int, tour moving to step " + rslt + 1);

														$_modalObject.off("hidden.bs.modal", funcModalHelper);
														return _this.goTo(rslt);
													}

													_this._debug("onModalHidden did not return false or int, continuing tour");
												}

												$_modalObject.off("hidden.bs.modal", funcModalHelper);

												// thanks to @eformx for finding this bug!
												if (_this._isLast())
												{
													_this._debug("Modal close reached end of tour");
													return _this.end();
												}
												else
												{
													_this._debug("Modal close: next step called");
													return _this.next();
												}
											};
										}(this, $modalObject);

				$modalObject.off("hidden.bs.modal", funcModalHelper).on("hidden.bs.modal", funcModalHelper);
			}

			// Helper function to actually show the popover using _showPopoverAndOverlay.
			// Note the flow - this is called immediately unless delayOnElement is set. If delayOnElement is set, this
			// func will be called if (a) the element appears, or (b) the element doesn't appear in the timeout.
			// Therefore this helper func MUST handle unintended orphans
			showStepHelper = (	function (_this)
								{
									return	function (e)
											{
												if (_this._isOrphan(step))
												{
													// Is this an unintended orphan?
													if (step.orphan === false && step.showIfUnintendedOrphan === false)
													{
														_this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element " + step.element + " does not exist or is hidden.");

														if(typeof(step.onElementUnavailable) == "function")
														{
															_this._debug("Calling onElementUnavailable callback");
															step.onElementUnavailable(_this, _this._current);
														}

														if (skipToPrevious) {
															_this._showPrevStep(true);
														} else {
															_this._showNextStep(true);
														}
														return;
													}

													if (step.orphan === false && step.showIfUnintendedOrphan === true)
													{
														// it's an unintended orphan, and global or step options still want to show it
														_this._debug("Show the unintended orphan step " + (_this._current + 1) + ". showIfUnintendedOrphan option is true.");
													}
													else
													{
														// It's an intended orphan
														_this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
													}
												}

												//console.log(step);

												if (step.autoscroll && !_this._isOrphan(step))
												{
													_this._scrollIntoView(i);
												}
												else
												{
													_this._showPopoverAndOverlay(i);
												}

												if (step.duration) {
													return _this.resume();
												}
											};
								})(this);


			// delay in millisec specified in step options
			showDelay = step.delay.show || step.delay;
			if ({}
				.toString.call(showDelay) === '[object Number]' && showDelay > 0) {
				this._debug("Wait " + showDelay + " milliseconds to show the step " + (this._current + 1));
				window.setTimeout((function (_this) {
						return function () {
							return _this._callOnPromiseDone(promise, showStepHelper);
						};
					})(this), showDelay);
			}
			else
			{
				if(step.delayOnElement)
				{
					// delay by element existence or max delay (default 2 sec)
					var $delayElement = null;
					var delayFunc = null;
					var _this = this;

					var revalidateDelayElement = function() {
						if(typeof(step.delayOnElement.delayElement) == "function")
							return step.delayOnElement.delayElement();
						else if(step.delayOnElement.delayElement == "element")
							return $(step.element);
						else
							return $(step.delayOnElement.delayElement);
					};
					var $delayElement = revalidateDelayElement();

					var delayElementLog = $delayElement.length > 0 ? $delayElement[0].tagName : step.delayOnElement.delayElement;

					var delayMax = (step.delayOnElement.maxDelay ? step.delayOnElement.maxDelay : 2000);
					this._debug("Wait for element " + delayElementLog + " visible or max " + delayMax + " milliseconds to show the step " + (this._current + 1));

					delayFunc = window.setInterval(	function()
													{
														_this._debug("Wait for element " + delayElementLog + ": checking...");
														if($delayElement.length === 0) {
															$delayElement = revalidateDelayElement();
														}
														if($delayElement.is(':visible'))
														{
															_this._debug("Wait for element " + delayElementLog + ": found, showing step");
															window.clearInterval(delayFunc);
															delayFunc = null;
															return _this._callOnPromiseDone(promise, showStepHelper);
														}
													}, 250);

					//	set max delay to greater than default interval check for element appearance
					if(delayMax < 250)
						delayMax = 251;

					// Set timer to kill the setInterval call after max delay time expires
					window.setTimeout(	function ()
										{
											if(delayFunc)
											{
												_this._debug("Wait for element " + delayElementLog + ": max timeout reached without element found");
												window.clearInterval(delayFunc);

												// showStepHelper will handle broken/missing/invisible element
												return _this._callOnPromiseDone(promise, showStepHelper);
											}
										}, delayMax);
				}
				else
				{
					// no delay by milliseconds or delay by time
					this._callOnPromiseDone(promise, showStepHelper);
				}
			}

			return promise;
		};

		Tour.prototype.getCurrentStepIndex = function () {
			return this._current;
		};

		Tour.prototype.setCurrentStep = function (value) {
			if (value != null)
			{
				this._current = value;
				this._setState('current_step', value);
			}
			else
			{
				this._current = this._getState('current_step');
				this._current = this._current === null ? 0 : parseInt(this._current, 10);
			}

			return this;
		};


		Tour.prototype._setState = function (key, value) {
			var e,
			keyName;
			if (this._options.storage) {
				keyName = this._options.name + "_" + key;
				try {
					this._options.storage.setItem(keyName, value);
				} catch (error) {
					e = error;
					if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
						this._debug('LocalStorage quota exceeded. State storage failed.');
					}
				}
				return this._options.afterSetState(keyName, value);
			} else {
				if (this._state == null) {
					this._state = {};
				}
				return this._state[key] = value;
			}
		};

		Tour.prototype._removeState = function (key) {
			var keyName;
			if (this._options.storage) {
				keyName = this._options.name + "_" + key;
				this._options.storage.removeItem(keyName);
				return this._options.afterRemoveState(keyName);
			} else {
				if (this._state != null) {
					return delete this._state[key];
				}
			}
		};

		Tour.prototype._getState = function (key) {
			var keyName,
			value;
			if (this._options.storage) {
				keyName = this._options.name + "_" + key;
				value = this._options.storage.getItem(keyName);
			} else {
				if (this._state != null) {
					value = this._state[key];
				}
			}
			if (value === void 0 || value === 'null') {
				value = null;
			}
			this._options.afterGetState(key, value);
			return value;
		};

		Tour.prototype._showNextStep = function (skipOrphan) {
			var promise,
			showNextStepHelper,
			step;

			var skipOrphan = skipOrphan || false;

			showNextStepHelper = (function (_this) {
				return function (e) {
					return _this.showStep(_this._current + 1);
				};
			})(this);

			promise = void 0;

			step = this.getStep(this._current);

			// only call the onNext handler if this is a click and NOT an orphan skip due to missing element
			if (skipOrphan === false &&  step.onNext != null)
			{
				var rslt = step.onNext(this);

				if(rslt === false)
				{
					this._debug("onNext callback returned false, preventing move to next step");
					return this.showStep(this._current);
				}

				promise = this._makePromise(rslt);
			}

			return this._callOnPromiseDone(promise, showNextStepHelper);
		};

		Tour.prototype._showPrevStep = function (skipOrphan) {
			var promise,
			showPrevStepHelper,
			step;

			var skipOrphan = skipOrphan || false;

			showPrevStepHelper = (function (_this) {
				return function (e) {
					return _this.showStep(step.prev);
				};
			})(this);

			promise = void 0;
			step = this.getStep(this._current);

			// only call the onPrev handler if this is a click and NOT an orphan skip due to missing element
			if (skipOrphan === false && step.onPrev != null)
			{
				var rslt = step.onPrev(this);

				if(rslt === false)
				{
					this._debug("onPrev callback returned false, preventing move to previous step");
					return this.showStep(this._current);
				}

				promise = this._makePromise(rslt);
			}

			return this._callOnPromiseDone(promise, showPrevStepHelper);
		};

		Tour.prototype._debug = function (text) {
			if (this._options.debug) {
				return window.console.log("[ Bootstrap Tourist: '" + this._options.name + "' ] " + text);
			}
		};

		Tour.prototype._isRedirect = function (host, path, location) {
			var currentPath;
			if ((host != null) && host !== '' && (({}
						.toString.call(host) === '[object RegExp]' && !host.test(location.origin)) || ({}
						.toString.call(host) === '[object String]' && this._isHostDifferent(host, location)))) {
				return true;
			}
			currentPath = [location.pathname, location.search, location.hash].join('');
			return (path != null) && path !== '' && (({}
					.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}
					.toString.call(path) === '[object String]' && this._isPathDifferent(path, currentPath)));
		};

		Tour.prototype._isHostDifferent = function (host, location) {
			switch ({}
				.toString.call(host)) {
			case '[object RegExp]':
				return !host.test(location.origin);
			case '[object String]':
				return this._getProtocol(host) !== this._getProtocol(location.href) || this._getHost(host) !== this._getHost(location.href);
			default:
				return true;
			}
		};

		Tour.prototype._isPathDifferent = function (path, currentPath) {
			return this._getPath(path) !== this._getPath(currentPath) || !this._equal(this._getQuery(path), this._getQuery(currentPath)) || !this._equal(this._getHash(path), this._getHash(currentPath));
		};

		Tour.prototype._isJustPathHashDifferent = function (host, path, location) {
			var currentPath;
			if ((host != null) && host !== '') {
				if (this._isHostDifferent(host, location)) {
					return false;
				}
			}
			currentPath = [location.pathname, location.search, location.hash].join('');
			if ({}
				.toString.call(path) === '[object String]') {
				return this._getPath(path) === this._getPath(currentPath) && this._equal(this._getQuery(path), this._getQuery(currentPath)) && !this._equal(this._getHash(path), this._getHash(currentPath));
			}
			return false;
		};

		Tour.prototype._redirect = function (step, i, path) {
			var href;
			if ($.isFunction(step.redirect)) {
				return step.redirect.call(this, path);
			} else {
				href = {}
				.toString.call(step.host) === '[object String]' ? "" + step.host + path : path;
				this._debug("Redirect to " + href);
				if (this._getState('redirect_to') === ("" + i)) {
					this._debug("Error redirection loop to " + path);
					this._removeState('redirect_to');
					if (step.onRedirectError != null) {
						return step.onRedirectError(this);
					}
				} else {
					this._setState('redirect_to', "" + i);
					return document.location.href = href;
				}
			}
		};

		// Tests if the step is orphan
		// Step can be "orphan" (unattached to any element) if specifically set as such in tour step options, or with an invalid/hidden element
		Tour.prototype._isOrphan = function (step)
		{
			var isOrphan = (step.orphan == true) || (step.element == null) || !$(step.element).length || $(step.element).is(':hidden') && ($(step.element)[0].namespaceURI !== 'http://www.w3.org/2000/svg');

			return isOrphan;
		};

		Tour.prototype._isLast = function () {
			return this._current >= this._options.steps.length - 1;
		};

		// wraps the calls to show the tour step in a popover and the background overlay.
		// Note this is ALSO called by scroll event handler. Individual funcs called will determine whether redraws etc are required.
		Tour.prototype._showPopoverAndOverlay = function (i)
		{
			var step;

			if (this.getCurrentStepIndex() !== i || this.ended()) {
				return;
			}
			step = this.getStep(i);

			// handles all show, hide and move of the background and highlight
			this._updateBackdropElements(step);

			// Show the preventInteraction overlay etc
			this._updateOverlayElements(step);

			// Required to fix the z index issue with BS select dropdowns
			this._fixBootstrapSelectPickerZindex(step);

			// Ensure this is called last, to allow preceeding calls to check whether current step popover is already visible.
			// This is required because this func is called by scroll event. showPopover creates the actual popover with
			// current step index as a class. Therefore all preceeding funcs can check if they are being called because of a
			// scroll event (popover class using current step index exists), or because of a step change (class doesn't exist).
			this._showPopover(step, i);

			if (step.onShown != null)
			{
				step.onShown(this);
			}

			return this;
		};

		// handles view of popover
		Tour.prototype._showPopover = function (step, i) {
			var $element,
			$tip,
			isOrphan,
			options,
			title,
			content,
			percentProgress,
			modalObject;

			isOrphan = this._isOrphan(step);


			// is this step already visible? _showPopover is called by _showPopoverAndOverlay, which is called by window scroll event. This
			// check prevents the continual flickering of the current tour step - original approach reloaded the popover every scroll event.
			// Why is this check here and not in _showPopoverAndOverlay? This allows us to selectively redraw elements on scroll.
			if($(document).find(".popover.tour-" + this._options.name + ".tour-" + this._options.name + "-" + this.getCurrentStepIndex()).length == 0)
			{
				// Step not visible, draw first time

				$(".tour-" + this._options.name).remove();

				step.template = this._template(step, i);

				if (isOrphan)
				{
					// Note: BS4 popper.js requires additional fiddling to work, see below where popOpts object is created
					step.element = 'body';
					step.placement = 'top';

					// If step is an intended or unintended orphan, and reflexOnly is set, show a warning.
					if(step.reflexOnly)
					{
						this._debug("Step is an orphan, and reflexOnly is set: ignoring reflexOnly");
					}
				}

				$element = $(step.element);

				$element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element");


				if (step.reflex && !isOrphan)
				{
					$element.addClass('tour-step-element-reflex');
					$element.off((this._reflexEvent(step.reflex)) + ".tour-" + this._options.name).on((this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function (_this) {
							return function ()
									{
										if (_this._isLast())
										{
											return _this.end();
										}
										else
										{
											return _this.next();
										}
									};
						})(this));

					if(step.reflexOnly)
					{
						// this pseudo-class is used to quickly identify reflexOnly steps in handlers / code that don't have access to tour.step (without
						// costly reloading) but need to know about reflexOnly. For example, obeying reflexOnly in keyboard handler. Solves
						// https://github.com/IGreatlyDislikeJavascript/bootstrap-tourist/issues/45
						$element.addClass('tour-step-element-reflexOnly');

						// Only disable the next button if this step is NOT an orphan.
						// This is difficult to achieve robustly because tour creator can use a custom template. Instead of trying to manually
						// edit the template - which must be a string to be passed to popover creation - use jquery to find the element, hide
						// it, then use the resulting DOM code/string to search and replace

						// Find "next" object (button, href, etc), create a copy
						var $objNext = $(step.template).find('[data-role="next"]').clone();

						if($objNext.length)
						{
							// Get the DOM code for the object
							var strNext = $objNext[0].outerHTML;

							$objNext.hide();

							// Get the DOM code for the hidden object
							var strHidden = $objNext[0].outerHTML;

							// string replace it in the template
							step.template = step.template.replace(strNext, strHidden);
						}
					}
				}


				title = step.title;
				content = step.content;
				percentProgress = parseInt(((i + 1) / this.getStepCount()) * 100);

				if(step.showProgressBar)
				{
					if(typeof(step.getProgressBarHTML) == "function")
					{
						content = step.getProgressBarHTML(percentProgress) + content;
					}
					else
					{
						content = '<div class="progress"><div class="progress-bar progress-bar-striped" role="progressbar" style="width: ' + percentProgress + '%;"></div></div>' + content;
					}
				}

				if(step.showProgressText)
				{
					if(typeof(step.getProgressTextHTML) == "function")
					{
						title += step.getProgressTextHTML(i, percentProgress, this.getStepCount());
					}
					else
					{
					    if(this._options.framework == "bootstrap3")
					    {
							title += '<span class="pull-right">' + (i + 1) + '/' + this.getStepCount() + '</span>';
					    }
					    if(this._options.framework == "bootstrap4")
					    {
							title += '<span class="float-right">' + (i + 1) + '/' + this.getStepCount() + '</span>';
					    }
					}
				}

				// Tourist v0.10 - split popOpts out of bootstrap popper instantiation due to BS3 / BS4 diverging requirements
				var popOpts = {
									placement: step.placement, // When auto is specified, it will dynamically reorient the popover.
									trigger: 'manual',
									title: title,
									content: content,
									html: true,
									//sanitize: false, // turns off all bootstrap sanitization of popover content, only use in last resort case - use whiteListAdditions instead!
									whiteList: this._options.sanitizeWhitelist, // ignored if sanitizeFn is specified
									sanitizeFn: this._options.sanitizeFunction,
									animation: step.animation,
									container: step.container,
									template: step.template,
									selector: step.element,
									//boundary: "viewport", // added for BS4 popper testing. Do not enable, creates visible jump on orphan step scroll to bottom
								};

				if(this._options.framework == "bootstrap4")
				{
					if(isOrphan)
					{
						// BS4 uses popper.js, which doesn't have a method of fixing the popper to the center of the viewport without an element. However
						// BS4 wrapper does some extra funky stuff that means we can't just replace the BS4 popper init code. Instead, fudge the popper
						// using the offset feature, which params don't seem to be documented properly!
						popOpts.offset = function(obj)
										{
											//console.log(obj);

											var top = Math.max(0, ( ($(window).height() - obj.popper.height) / 2) );
											var left = Math.max(0, ( ($(window).width() - obj.popper.width) / 2) );

											obj.popper.position="fixed";
											obj.popper.top = top;
											obj.popper.bottom = top + obj.popper.height;
											obj.popper.left = left;
											obj.popper.right = top + obj.popper.width;
											return obj;
										};
					}
					else
					{
						// BS3 popover accepts jq object or string literal. BS4 popper.js of course doesn't, just to make life extra irritating.
						popOpts.selector = "#" + step.element[0].id;

						// Allow manual repositioning of the popover
						// THIS DOESN'T WORK - popper.js will only adjust on one axis even if both axis are specified...
						if(step.positioning.adjustRelative !== null && step.positioning.adjustRelative.length > 0)
						{
							if(typeof step.positioning.adjustRelative == "function")
							{
								popOpts.offset = step.positioning.adjustRelative();
							}
							else
							{
								popOpts.offset = step.positioning.adjustRelative;
							}
						}
					}
				}

				$element.popover(popOpts);
				$element.popover('show');

				if(this._options.framework == "bootstrap3")
				{
					$tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();

					// For BS3 only. BS4 popper.js reverts this change
					if ($element.css('position') === 'fixed')
					{
						$tip.css('position', 'fixed');
					}

					if (isOrphan)
					{
						this._center($tip);
						$tip.css('position', 'fixed');
					}
					else
					{
						this._reposition($tip, step);
					}
				}

				if(this._options.framework == "bootstrap4")
				{
					$tip = $( ($element.data('bs.popover') ? $element.data('bs.popover').getTipElement() : $element.data('popover').getTipElement() ) );
				}

				$tip.attr('id', step.id);

				this._debug("Step " + (this._current + 1) + " of " + this._options.steps.length);
			}
			else
			{
				// Step is already visible, something has requested a redraw. Uncomment code to force redraw on scroll etc
				//$element = $(step.element);
				//$tip = $element.data('bs.popover') ? $element.data('bs.popover').tip() : $element.data('popover').tip();

				if (isOrphan)
				{
					// unnecessary re-call, when tour step is set up centered it's fixed to the middle.
					//this._center($tip);
				}
				else
				{
					// Add some code to shift the popover wherever is required.
					// NOTE: this approach works for BS3 ONLY. BS4 with popper.js requires manipulation of offset, see popOpts.offset above.
					//this._reposition($tip, step);
				}
			}
		};

		Tour.prototype._template = function (step, i) {
			var $navigation,
			$next,
			$prev,
			$resume,
			$template,
			template;
			template = step.template;
			if (this._isOrphan(step) && {}
				.toString.call(step.orphan) !== '[object Boolean]') {
				template = step.orphan;
			}
			$template = $.isFunction(template) ? $(template(i, step)) : $(template);
			$navigation = $template.find('.popover-navigation');
			$prev = $navigation.find('[data-role="prev"]');
			$next = $navigation.find('[data-role="next"]');
			$resume = $navigation.find('[data-role="pause-resume"]');
			if (this._isOrphan(step)) {
				$template.addClass('orphan');
			}
			$template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i);
			if (step.reflex) {
				$template.addClass("tour-" + this._options.name + "-reflex");
			}
			if (step.prev < 0) {
				$prev.addClass('disabled').prop('disabled', true).prop('tabindex', -1);
			}
			if (step.next < 0) {
				$next.addClass('disabled').prop('disabled', true).prop('tabindex', -1);
			}
			// Cannot do this here due to new option showIfUnintendedOrphan - an unintended orphan with reflex/reflexonly will create a
			// tour step that can't be moved on from! This must be done in showPopover, which is called after the step is loaded and any
			// delayOnElement timeouts etc have occurred, meaning we know for certain in _showPopover whether the step is an orphan
//			if (step.reflexOnly) {
//				$next.hide();
//			}
			if (!step.duration) {
				$resume.remove();
			}
			return $template.clone().wrap('<div>').parent().html();
		};

		Tour.prototype._reflexEvent = function (reflex) {
			if ({}
				.toString.call(reflex) === '[object Boolean]') {
				return 'click';
			} else {
				return reflex;
			}
		};

		Tour.prototype._reposition = function ($tip, step) {
			var offsetBottom,
			offsetHeight,
			offsetRight,
			offsetWidth,
			originalLeft,
			originalTop,
			tipOffset;
			offsetWidth = $tip[0].offsetWidth;
			offsetHeight = $tip[0].offsetHeight;

			tipOffset = $tip.offset();
			originalLeft = tipOffset.left;
			originalTop = tipOffset.top;

			offsetBottom = $(document).height() - tipOffset.top - $tip.outerHeight();
			if (offsetBottom < 0) {
				tipOffset.top = tipOffset.top + offsetBottom;
			}

			offsetRight = $('html').outerWidth() - tipOffset.left - $tip.outerWidth();
			if (offsetRight < 0) {
				tipOffset.left = tipOffset.left + offsetRight;
			}
			if (tipOffset.top < 0) {
				tipOffset.top = 0;
			}
			if (tipOffset.left < 0) {
				tipOffset.left = 0;
			}

			$tip.offset(tipOffset);

			if (step.placement === 'bottom' || step.placement === 'top') {
				if (originalLeft !== tipOffset.left) {
					return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, 'left');
				}
			} else {
				if (originalTop !== tipOffset.top) {
					return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, 'top');
				}
			}
		};

		Tour.prototype._center = function ($tip)
		{
			$tip.css('top', $(window).outerHeight() / 2 - $tip.outerHeight() / 2);

			return $tip.css('left', $(window).outerWidth() / 2 - $tip.outerWidth() / 2);
		};

		Tour.prototype._replaceArrow = function ($tip, delta, dimension, position) {
			return $tip.find('.arrow').css(position, delta ? 50 * (1 - delta / dimension) + '%' : '');
		};

		Tour.prototype._scrollIntoView = function (i) {
			var $element,
			$window,
			counter,
			height,
			offsetTop,
			scrollTop,
			step,
			windowHeight;
			step = this.getStep(i);
			$element = $(step.element);

			if(this._isOrphan(step))
			{
				// If this is an orphan step, don't auto-scroll. Orphan steps are now css fixed to center of window
				return this._showPopoverAndOverlay(i);
			}

			if (!$element.length)
			{
				return this._showPopoverAndOverlay(i);
			}

			$window = $(window);
			offsetTop = $element.offset().top;
			height = $element.outerHeight();
			windowHeight = $window.height();
			scrollTop = 0;
			switch (step.placement) {
			case 'top':
				scrollTop = Math.max(0, offsetTop - (windowHeight / 2));
				break;
			case 'left':
			case 'right':
				scrollTop = Math.max(0, (offsetTop + height / 2) - (windowHeight / 2));
				break;
			case 'bottom':
				scrollTop = Math.max(0, (offsetTop + height) - (windowHeight / 2));
			}
			this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + ".");
			counter = 0;
			return $('body, html').stop(true, true).animate({
				scrollTop: Math.ceil(scrollTop)
			}, (function (_this) {
					return function () {
						if (++counter === 2) {
							_this._showPopoverAndOverlay(i);
							return _this._debug("Scroll into view.\nAnimation end element offset: " + ($element.offset().top) + ".\nWindow height: " + ($window.height()) + ".");
						}
					};
				})(this));
		};


		// Note: this method is not required, but remains here in case any future forkers want to reinstate the code that moves a non-orphan popover
		// when window is scrolled
//		Tour.prototype._onScroll = function (callback, timeout) {
//			return $(window).on("scroll.tour-" + this._options.name, function () {
//				clearTimeout(timeout);
//				return timeout = setTimeout(callback, 100);
//			});
//		};

		Tour.prototype._initMouseNavigation = function () {
			var _this;
			_this = this;
			return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']", (function (_this) {
					return function (e) {
						e.preventDefault();
						return _this.next();
					};
				})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']", (function (_this) {
					return function (e) {
						e.preventDefault();
						if (_this._current > 0) {
							return _this.prev();
						}
					};
				})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function (_this) {
					return function (e) {
						e.preventDefault();
						return _this.end();
					};
				})(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function (e) {
				var $this;
				e.preventDefault();
				$this = $(this);
				$this.text(_this._paused ? $this.data('pause-text') : $this.data('resume-text'));
				if (_this._paused) {
					return _this.resume();
				} else {
					return _this.pause();
				}
			});
		};

		Tour.prototype._initKeyboardNavigation = function () {
			if (!this._options.keyboard) {
				return;
			}
			return $(document).on("keyup.tour-" + this._options.name, (function (_this) {
					return function (e) {
						if (!e.which) {
							return;
						}
						switch (e.which)
						{
							case 39:
								// arrow right
								if($(".tour-step-element-reflexOnly").length == 0)
								{
									e.preventDefault();
									if(_this._isLast())
									{
										return _this.end();
									}
									else
									{
										return _this.next();
									}
								}

								break;

							case 37:
								// arrow left
								if($(".tour-step-element-reflexOnly").length == 0)
								{
									e.preventDefault();
									if (_this._current > 0)
									{
										return _this.prev();
									}
								}
								break;

							case 27:
								// escape
								e.preventDefault();
								return _this.end();
								break;
						}
					};
				})(this));
		};

		// If param is a promise, returns the promise back to the caller. Otherwise returns null.
		// Only purpose is to make calls to _callOnPromiseDone() simple - first param of _callOnPromiseDone()
		// accepts either null or a promise to smart call either promise or straight callback. This
		// pair of funcs therefore allows easy integration of user code to return callbacks or promises
		Tour.prototype._makePromise = function (possiblePromise)
		{
			if (possiblePromise && $.isFunction(possiblePromise.then))
			{
				return possiblePromise;
			}
			else
			{
				return null;
			}
		};

		// Creates a promise wrapping the callback if valid promise is provided as first arg. If
		// first arg is not a promise, simply uses direct function call of callback.
		Tour.prototype._callOnPromiseDone = function (promise, callback, arg)
		{
			if (promise)
			{
				return promise.then(
										(function (_this)
										{
											return function (e)
											{
												return callback.call(_this, arg);
											};
										}
										)(this)
									);
			}
			else
			{
				return callback.call(this, arg);
			}
		};

		// Bootstrap Select custom draws the drop down, force the Z index between Tour overlay and popoper
 		Tour.prototype._fixBootstrapSelectPickerZindex = function(step)
		{
			if(this._isOrphan(step))
			{
				// If it's an orphan step, it can't be a selectpicker element
				return;
			}

			// is the current step already visible?
			if($(document).find(".popover.tour-" + this._options.name + ".tour-" + this._options.name + "-" + this.getCurrentStepIndex()).length != 0)
			{
				// don't waste time redoing the fix
				return;
			}

			var $selectpicker;
			// is this element or child of this element a selectpicker
			if($(step.element)[0].tagName.toLowerCase() == "select")
			{
				$selectpicker = $(step.element);
			}
			else
			{
				$selectpicker = $(step.element).find("select:first");
			}

			// is this selectpicker a bootstrap-select: https://github.com/snapappointments/bootstrap-select/
			if($selectpicker.length > 0 && $selectpicker.parent().hasClass("bootstrap-select"))
			{
				this._debug("Fixing Bootstrap SelectPicker");
				// set zindex to open dropdown over background element and at zindex of highlight element
				$selectpicker.parent().css("z-index", "1111");

				// store the element for other calls. Mainly for when step is hidden, selectpicker must be unfixed / z index reverted to avoid visual issues.
				// storing element means we don't need to find it again later
				this._setStepFlag(this.getCurrentStepIndex(), "elementBootstrapSelectpicker", $selectpicker);
			}
		};

		// Revert the Z index between Tour overlay and popoper
 		Tour.prototype._unfixBootstrapSelectPickerZindex = function(step)
		{
			var $selectpicker = this._getStepFlag(this.getCurrentStepIndex(), "elementBootstrapSelectpicker");
			if($selectpicker)
			{
				this._debug("Unfixing Bootstrap SelectPicker");
				// set zindex to open dropdown over background element
				$selectpicker.parent().css("z-index", "auto");
			}
		};


		// ===================================================================================================================================================
		// NEW OVERLAY CODE
		//
		// NOTE: "backdrop" refers to all the elements required to create the "dark background with a highlight" function, i.e.: a background div and
		// a highlight div.
		// ===================================================================================================================================================

		// Actually creates the 3 divs for functionality
		Tour.prototype._createOverlayElements = function ()
        {
			// the .substr(1) is because the DOMID_ consts start with # for jq object ease...
			var $backdrop = $('<div class="tour-backdrop" id="' + DOMID_BACKDROP.substr(1) + '"></div>');
            var $highlight = $('<div class="tour-highlight" id="' + DOMID_HIGHLIGHT.substr(1) + '" style="width:0px;height:0px;top:0px;left:0px;"></div>');

			// _updateOverlayElements creates and destroys prevent div as required
            //var $preventDiv = $('<div class="tour-prevent" id="' + DOMID_PREVENT.substr(1) + '" style="width:0px;height:0px;top:0px;left:0px;"></div>');

            //var $debug = $('<!-- debug -->');
			//$("body").append($debug);

            if ($(DOMID_BACKDROP).length === 0)
            {
                $(this._options.backdropContainer).append($backdrop);
            }
            if ($(DOMID_HIGHLIGHT).length === 0)
            {
                $(this._options.backdropContainer).append($highlight);
            }

//            if ($(DOMID_PREVENT).length === 0)
//            {
//                $(this._options.backdropContainer).append($preventDiv);
//            }
        };

		Tour.prototype._destroyOverlayElements = function(step)
        {
			$(DOMID_BACKDROP).remove();
			$(DOMID_HIGHLIGHT).remove();
			$(DOMID_PREVENT).remove();

			$(".tour-highlight-element").removeClass("tour-highlight-element");
		};

		// Hides the background and highlight. Caller is responsible for ensuring step wants hidden
		// backdrop
		Tour.prototype._hideBackdrop = function(step)
        {
			var step = step || null;

			if(step)
			{
				// No backdrop? No need for highlight
				this._hideHighlightOverlay(step);

				// Does global or this step specify a function for the backdrop layer hide?
				if(typeof step.backdropOptions.animation.backdropHide == "function")
				{
					// pass DOM element jq object to function
					step.backdropOptions.animation.backdropHide($(DOMID_BACKDROP));
				}
				else
				{
					// must be a CSS class
					$(DOMID_BACKDROP).addClass(step.backdropOptions.animation.backdropHide);
					$(DOMID_BACKDROP).hide(0,	function()
												{
													$(this).removeClass(step.backdropOptions.animation.backdropHide);
												});
				}

			}
			else
			{
				$(DOMID_BACKDROP).hide(0);
				$(DOMID_HIGHLIGHT).hide(0);
                $(DOMID_BACKDROP_TEMP).remove();
                $(DOMID_HIGHLIGHT_TEMP).remove();
			}
        };

		// Shows the backdrop (backdrop + highlight elements if not orphan). Caller is responsible for ensuring step really wants a visible
		// backdrop
		Tour.prototype._showBackdrop = function (step)
        {
			var step = step || null;

			// Ensure we're always starting with a clean, hidden backdrop - this ensures any previous step.backdropOptions.animation.* functions
			// haven't messed with the classes
			$(DOMID_BACKDROP).removeClass().addClass("tour-backdrop").hide(0);

			if(step)
			{
				// Does global or this step specify a function for the backdrop layer show?
				if(typeof step.backdropOptions.animation.backdropShow == "function")
				{
					// pass DOM element jq object to function
					step.backdropOptions.animation.backdropShow($(DOMID_BACKDROP));
				}
				else
				{
					// must be a CSS class
					$(DOMID_BACKDROP).addClass(step.backdropOptions.animation.backdropShow);
					$(DOMID_BACKDROP).show(0,	function()
												{
													$(this).removeClass(step.backdropOptions.animation.backdropShow);
												});
				}


				// Now handle the highlight layer. The backdrop and highlight layers operate together to create the visual backdrop, but are handled
				// as separate DOM and code elements.
				if(this._isOrphan(step))
				{
					// Orphan step will never require a highlight, as there's no element
					if($(DOMID_HIGHLIGHT).is(':visible'))
					{
						this._hideHighlightOverlay(step);
					}
					else
					{
						// orphan step with highlight layer already hidden - do nothing
					}
				}
				else
				{
					// Not an orphan, so requires a highlight layer.
					if($(DOMID_HIGHLIGHT).is(':visible'))
					{
						// Already visible, so this is a transition - move from 1 position to another. This shouldn't be possible,
						// as a call to showBackdrop() logically means the backdrop is hidden, therefore the highlight is hidden. Kept for safety.
						this._positionHighlightOverlay(step);
					}
					else
					{
						// Not visible, this is a show
						this._showHighlightOverlay(step);
					}
				}

			}
			else
			{
				$(DOMID_BACKDROP).show(0);
				$(DOMID_HIGHLIGHT).show(0);
			}
        };

		// Creates an object representing the current step with a subset of properties and functions, for
		// tour creator to use when passing functions to step.backdropOptions.animation options
		Tour.prototype._createStepSubset = function (step)
		{
			var _this = this;
			var _stepElement = $(step.element);

			var stepSubset =	{
									element:				_stepElement,
									container:				step.container,
									autoscroll:				step.autoscroll,
									backdrop:				step.backdrop,
									preventInteraction:		step.preventInteraction,
									isOrphan:				this._isOrphan(step),
									orphan:					step.orphan,
									showIfUnintendedOrphan:	step.showIfUnintendedOrphan,
									duration:				step.duration,
									delay:					step.delay,
									fnPositionHighlight:	function()
															{
																_this._debug("Positioning highlight (fnPositionHighlight) over step element " + _stepElement[0].id + ":\nWidth = " + _stepElement.outerWidth() + ", height = " + _stepElement.outerHeight() + "\nTop: " + _stepElement.offset().top + ", left: " + _stepElement.offset().left);
																$(DOMID_HIGHLIGHT).width(_stepElement.outerWidth()).height(_stepElement.outerHeight()).offset(_stepElement.offset());
															},

								};

			return stepSubset;
		};


		// Shows the highlight and applies class to highlighted element
		Tour.prototype._showHighlightOverlay = function (step)
		{
			// safety check, ensure no other elem has the highlight class
			var $elemTmp = $(".tour-highlight-element");
			if($elemTmp.length > 0)
			{
				$elemTmp.removeClass('tour-highlight-element');
			}

			// Is this a modal - we must set the zindex on the modal element, not the modal-content element
			var $modalCheck = $(step.element).parents(".modal:first");
			if($modalCheck.length)
			{
				$modalCheck.addClass('tour-highlight-element');
			}
			else
			{
				$(step.element).addClass('tour-highlight-element');
			}

			// Ensure we're always starting with a clean, hidden highlight - this ensures any previous step.backdropOptions.animation.* functions
			// haven't messed with the classes
			$(DOMID_HIGHLIGHT).removeClass().addClass("tour-highlight").hide(0);

			if(typeof step.backdropOptions.animation.highlightShow == "function")
			{
				// pass DOM element jq object to function. Function is completely responsible for positioning and showing.
				// dupe the step to avoid function messing with original object.
				step.backdropOptions.animation.highlightShow($(DOMID_HIGHLIGHT), this._createStepSubset(step));
			}
			else
			{
				// must be a CSS class. Give a default animation
				$(DOMID_HIGHLIGHT).css(	{
											"opacity": step.backdropOptions.highlightOpacity,
											"background-color": step.backdropOptions.highlightColor
										});

				$(DOMID_HIGHLIGHT).width(0).height(0).offset({ top: 0, left: 0 });
				$(DOMID_HIGHLIGHT).show(0);
				$(DOMID_HIGHLIGHT).addClass(step.backdropOptions.animation.highlightShow);

				$(DOMID_HIGHLIGHT).width($(step.element).outerWidth()).height($(step.element).outerHeight()).offset($(step.element).offset());
				$(DOMID_HIGHLIGHT).one('webkitAnimationEnd oanimationend msAnimationEnd animationend',  function()
																										{
																											$(DOMID_HIGHLIGHT).removeClass(step.backdropOptions.animation.highlightShow);
																										});
			}
		};

		// Repositions a currently visible highlight
		Tour.prototype._positionHighlightOverlay = function (step)
		{
			// safety check, ensure no other elem has the highlight class
			var $elemTmp = $(".tour-highlight-element");
			if($elemTmp.length > 0)
			{
				$elemTmp.removeClass('tour-highlight-element');
			}

			// Is this a modal - we must set the zindex on the modal element, not the modal-content element
			var $modalCheck = $(step.element).parents(".modal:first");
			if($modalCheck.length)
			{
				$modalCheck.addClass('tour-highlight-element');
			}
			else
			{
				$(step.element).addClass('tour-highlight-element');
			}

			if(typeof step.backdropOptions.animation.highlightTransition == "function")
			{
				// Don't clean existing classes - this allows tour coder to fully control the highlight between steps

				// pass DOM element jq object to function. Function is completely responsible for positioning and showing.
				// dupe the step to avoid function messing with original object.
				step.backdropOptions.animation.highlightTransition($(DOMID_HIGHLIGHT), this._createStepSubset(step));
			}
			else
			{
				// must be a CSS class. Start by cleaning all other classes
				$(DOMID_HIGHLIGHT).removeClass().addClass("tour-highlight");

				// obey step options
				$(DOMID_HIGHLIGHT).css(	{
											"opacity": step.backdropOptions.highlightOpacity,
											"background-color": step.backdropOptions.highlightColor
										});

				// add transition animations
				$(DOMID_HIGHLIGHT).addClass(step.backdropOptions.animation.highlightTransition);
				$(DOMID_HIGHLIGHT).width($(step.element).outerWidth()).height($(step.element).outerHeight()).offset($(step.element).offset());
				$(DOMID_HIGHLIGHT).one('webkitAnimationEnd oanimationend msAnimationEnd animationend',  function()
																										{
																											$(DOMID_HIGHLIGHT).removeClass(step.backdropOptions.animation.highlightTransition);
																										});
			}
		};

		Tour.prototype._hideHighlightOverlay = function (step)
		{
			// remove the highlight class
			$(".tour-highlight-element").removeClass('tour-highlight-element');

			if(typeof step.backdropOptions.animation.highlightHide == "function")
			{
				// pass DOM element jq object to function. Function is completely responsible for positioning and showing.
				// dupe the step to avoid function messing with original object.
				step.backdropOptions.animation.highlightHide($(DOMID_HIGHLIGHT), this._createStepSubset(step));
			}
			else
			{
				// must be a CSS class
				$(DOMID_HIGHLIGHT).addClass(step.backdropOptions.animation.highlightHide);
				//$(DOMID_HIGHLIGHT).width(0).height(0).offset({ top: 0, left: 0 });
				$(DOMID_HIGHLIGHT).one('webkitAnimationEnd oanimationend msAnimationEnd animationend',  function()
																										{
																											// ensure we end with a clean div
																											$(DOMID_HIGHLIGHT).removeClass().addClass("tour-highlight");
																											$(DOMID_HIGHLIGHT).hide(0);
																										});
			}
		};

		// Moves, shows or hides the backdrop and highlight element to match the specified step
		Tour.prototype._updateBackdropElements = function (step)
        {
			// Change to backdrop visibility required? (step.backdrop != current $(DOMID_BACKDROP) visibility)
			if(step.backdrop != $(DOMID_BACKDROP).is(':visible'))
			{
				// step backdrop not in sync with actual backdrop. Deal with it!
				if(step.backdrop)
				{
					// handles both the background div and the highlight layer
					this._showBackdrop(step);
				}
				else
				{
					this._hideBackdrop(step);
				}
			}
			else
			{
				// backdrop is in the correct state (visible/non visible) for this step
				if(step.backdrop)
				{
					// Step includes backdrop, and backdrop is already visible.
					// Is this step an orphan (i.e.: no highlight)?
					if(this._isOrphan(step))
					{
						// Orphan doesn't require highlight as no element. Is the highlight currently visible? (from the previous step)
						if($(DOMID_HIGHLIGHT).is(':visible'))
						{
							// Need to hide it
							this._hideHighlightOverlay(step);
						}
						else
						{
							// Highlight not visible, not required. Do nothing.
						}
					}
					else
					{
						// Highlight required
						if($(DOMID_HIGHLIGHT).is(':visible'))
						{
							// Transition it
							this._positionHighlightOverlay(step);
						}
						else
						{
							// Show it
							this._showHighlightOverlay(step);
						}
					}
				}
				else
				{
					// Step does not include backdrop, backdrop is already hidden.
					// Ensure highlight is also hidden - safety check as hideBackdrop also hides highlight
					if($(DOMID_HIGHLIGHT).is(':visible'))
					{
						this._hideHighlightOverlay(step);
					}
				}
			}

			// purpose of this code is due to elements with position: fixed and z-index: https://github.com/IGreatlyDislikeJavascript/bootstrap-tourist/issues/38
			$(DOMID_BACKDROP_TEMP).remove();
			$(DOMID_HIGHLIGHT_TEMP).remove();
            if (step.backdropOptions.backdropSibling == true)
            {
                $(DOMID_HIGHLIGHT).addClass('tour-behind');
                $(DOMID_BACKDROP).addClass('tour-zindexFix');
                $(DOMID_HIGHLIGHT).clone().prop('id', DOMID_HIGHLIGHT_TEMP.substring(1)).removeClass('tour-behind').insertAfter(".tour-highlight-element");
                $(DOMID_BACKDROP).clone().prop('id', DOMID_BACKDROP_TEMP.substring(1)).removeClass('tour-zindexFix').insertAfter(".tour-highlight-element");
            }
            else
            {
                $(DOMID_HIGHLIGHT).removeClass('tour-behind');
                $(DOMID_BACKDROP).removeClass('tour-zindexFix');
            }
        };

		// Updates visibility of the preventInteraction div and any other overlay elements added in future features
		Tour.prototype._updateOverlayElements = function (step)
		{
			// check if the popover for the current step already exists (is this a redraw)
			if (step.preventInteraction)
			{
                this._debug("preventInteraction == true, adding overlay");
				if ($(DOMID_PREVENT).length === 0)
				{
                    $('<div class="tour-prevent" id="' + DOMID_PREVENT.substr(1) + '" style="width:0px;height:0px;top:0px;left:0px;"></div>').insertAfter(DOMID_HIGHLIGHT);
                }

                $(DOMID_PREVENT).width($(step.element).outerWidth()).height($(step.element).outerHeight()).offset($(step.element).offset());
            }
			else
			{
                $(DOMID_PREVENT).remove();
            }

		};


		// ===================================================================================================================================================
		// END NEW OVERLAY CODE
		// ===================================================================================================================================================


		Tour.prototype._clearTimer = function () {
			window.clearTimeout(this._timer);
			this._timer = null;
			return this._duration = null;
		};


		// =============================================================================================================================

		Tour.prototype._getProtocol = function (url) {
			url = url.split('://');
			if (url.length > 1) {
				return url[0];
			} else {
				return 'http';
			}
		};

		Tour.prototype._getHost = function (url) {
			url = url.split('//');
			url = url.length > 1 ? url[1] : url[0];
			return url.split('/')[0];
		};

		Tour.prototype._getPath = function (path) {
			return path.replace(/\/?$/, '').split('?')[0].split('#')[0];
		};

		Tour.prototype._getQuery = function (path) {
			return this._getParams(path, '?');
		};

		Tour.prototype._getHash = function (path) {
			return this._getParams(path, '#');
		};

		Tour.prototype._getParams = function (path, start) {
			var j,
			len,
			param,
			params,
			paramsObject;
			params = path.split(start);
			if (params.length === 1) {
				return {};
			}
			params = params[1].split('&');
			paramsObject = {};
			for (j = 0, len = params.length; j < len; j++) {
				param = params[j];
				param = param.split('=');
				paramsObject[param[0]] = param[1] || '';
			}
			return paramsObject;
		};

		Tour.prototype._equal = function (obj1, obj2) {
			var j,
			k,
			len,
			obj1Keys,
			obj2Keys,
			v;
			if ({}
				.toString.call(obj1) === '[object Object]' && {}
				.toString.call(obj2) === '[object Object]') {
				obj1Keys = Object.keys(obj1);
				obj2Keys = Object.keys(obj2);
				if (obj1Keys.length !== obj2Keys.length) {
					return false;
				}
				for (k in obj1) {
					v = obj1[k];
					if (!this._equal(obj2[k], v)) {
						return false;
					}
				}
				return true;
			} else if ({}
				.toString.call(obj1) === '[object Array]' && {}
				.toString.call(obj2) === '[object Array]') {
				if (obj1.length !== obj2.length) {
					return false;
				}
				for (k = j = 0, len = obj1.length; j < len; k = ++j) {
					v = obj1[k];
					if (!this._equal(v, obj2[k])) {
						return false;
					}
				}
				return true;
			} else {
				return obj1 === obj2;
			}
		};

		return Tour;

	})();
	return Tour;
});

/**
 * Handle frontend and backend tutorial.
 * Statuses are:
 * -1 Inactive
 * 0 Never launched
 * 1 In use
*/

$(function() {

  if (typeof frontendTutorialState !== "undefined" && frontendTutorialState == 1)
  {
    launchFrontendTutorial();
  }
  else if (typeof backendTutorialState !== "undefined" && backendTutorialState == 1)
  {
    launchBackendTutorial();
  }
  else
  {
    if (!isAdminPage && typeof frontendTutorialState !== "undefined" && frontendTutorialState != -1)
      launchTutorialAlert();
    else if (isAdminPage && typeof backendTutorialState !== "undefined" && backendTutorialState != -1)
      launchTutorialAlert();
  }

  $(document).on('click', '.frontend-tutorial', function(e) {
    e.preventDefault();
    activateTutorial('frontend');
  });

  $(document).on('click', '.backend-tutorial', function(e) {
    e.preventDefault();
    activateTutorial('backend');
  });

  $(document).on('click', '.disable-tutorials', function(e) {
    e.preventDefault();

    $('#tutorialAlertModal').modal('hide');

    var promise = []
    promise.push(saveTutorialState('frontend', -1));
    promise.push(saveTutorialState('backend', -1));

    Promise.all(promise).then(function(response) {
      Swal.fire({
        type: 'info',
        text: 'Vous pouvez à tout moment accéder à nouveau aux guides interactifs depuis le bouton "Aide" en haut à droite de l\'écran.',
        backdrop: true,
        confirmButtonText: editorI18n.general.close,
        onAfterClose: function() {
          window.location.reload();
        }
      });
    });
  });
});

function activateTutorial(category)
{
  var promise = saveTutorialState(category, 1);

  promise.then(function(response) {
    if (category == 'frontend')
      window.location.href = '/';
    else
      window.location.href = '/admin/';
  });
}

function launchTutorialAlert() {
  $('#tutorialAlertModal').modal();
} // launchTutorialAlert()

function saveTutorialState(category, state)
{
  return new Promise(function (resolve, reject) {
    $.getJSON(ajaxURL + 'cms_setTutorialState', {
      category: category,
      state: state
    }, function (json) {
      if (json.error)
        showError(json.message);
      else {
        resolve(true);
      }
    });
  });
} // saveTutorialState()
$(function() {
  // Comments pagination
  $('.comments-system').on('click', '.pagination-element', function(e) {
    e.preventDefault();

    var pageID = $('.comments-system').attr('data-pageID');
    var moduleID = $('.comments-system').attr('data-moduleID');
    var reference1 = $('.comments-system').attr('data-reference1');
    var reference2 = $('.comments-system').attr('data-reference2');
    var pagination = $(this).attr('data-page');

    $.getJSON(ajaxURL+'cms_getComments', {
      pageID: pageID,
      moduleID: moduleID,
      reference1: reference1,
      reference2: reference2,
      pagination: pagination
    }, function(json) {
			if (json.error)
      {
        showError(json.data.message);
      }
      else
      {
        // Delete comments
        $('.comment-list .comment').remove();

        // Insert new page comments
        $('.comment-list').prepend(json.data.comments);

        // Update pagination
        $('.comments-system .page-pagination').replaceWith(json.data.pagination);
      }
		});
  });

  $('.comments-system').on('click', '.answer-comment', function(e) {
    e.preventDefault();

    // Remove form inside others comments
    $('.comment').find('.comment-form').remove();

    // Display form inside beneath comment
    var comment = $(this).parents('.comment');
    $('.comment-form').clone().appendTo(comment.find('.sub-comments'));

    comment.find('.close-answer-comment').show();
    
    // Render captcha
    comment.find('.h-captcha').empty();
    renderCaptcha();

    // Update variables
    comment.find('input[name="parentID"]').val(comment.attr('data-commentID'));
  });

  $('.comments-system').on('click', '.close-answer-comment', function(e) {
    e.preventDefault();

    var comment = $(this).parents('.comment');
    comment.find('.comment-form').remove();
  });

  $('.comments-system').on('click', '.sub-comments-count', function(e) {
    e.preventDefault();
    var parentID = $(this).parents('.comment').attr('data-commentID');
    loadSubComments(parentID);
  });

  $('.comments-system').on('click', '.load-more', function(e) {
    e.preventDefault();
    var parentID = $(this).parents('.comment').attr('data-commentID');
    loadSubComments(parentID);
  });

  $('.comments-system').on('click', '.update-comment', function(e) {
    e.preventDefault();
    var commentID = $(this).parents('.comment').attr('data-commentID');
    updateComment(commentID);
  });

  // Delete comment
  $('.comments-system').on('click', '.delete-comment', function(e) {
    e.preventDefault();
    var commentID = $(this).parents('.comment').attr('data-commentID');
    deleteComment(commentID);
  });

  // Delete comment from backend
  $('.comment-list').on('click', '.delete-comment', function(e) {
    e.preventDefault();
    var commentID = $(this).parents('.comment').attr('data-commentID');
    deleteComment(commentID);
  });

  $('.comments-system').on('click', '.comment-form input[type="button"]', function() {
    var data = $(this).parents('form').serialize();
    var isLogged = $(this).parents('form').find('input[name="isLogged"]').val();

    // Add captcha value for not logged users
    if (typeof hcaptcha !== 'undefined' && !isLogged)
      data['h-captcha-response'] = hcaptcha.getResponse();

    $.getJSON(ajaxURL+'cms_insertComment', data, function(json) {
			if (json.error)
      {
        showError(json.data.message);
      }
      else
      {
        $('.comment-list .empty').hide();

        if (json.data.parentID)
        {
          var destination = $('.comment[data-commentID="'+ json.data.parentID +'"] .sub-comments');
          destination.find('.comment-form').remove();
        }
        else
        {
          var destination = $('.comment-list');
        }

        destination.prepend(json.data.comment);
        showSuccess(json.data.message);

        $('.comment-form').find('input[type="text"], textarea').val('');
        lazyLoading();

        if (typeof hcaptcha !== 'undefined' && !isLogged)
          hcaptcha.reset();
      }
		});
  });
});


function loadSubComments(parentID)
{
  var comment = $('.comment[data-commentID="'+ parentID +'"]');
  var pagination = parseInt(comment.attr('data-pagination')) + 1;

  $.getJSON(ajaxURL+'cms_getMoreComments', {
    parentID: parentID,
    pagination: pagination
  }, function(json) {
    if (json.error)
    {
      showError(json.data.message);
    }
    else
    {
      comment.find('.comment-form').remove();
      comment.find('.sub-comments').append(json.data.comments);
      comment.attr('data-pagination', pagination);
      comment.find('.sub-comments-count').remove();
      comment.find('.load-more').remove();

      // Check if load more should be displayed
      if (comment.find('.sub-comments').find('.comment').length < comment.attr('data-subCommentsCount'))
        comment.find('.sub-comments').append('<div class="load-more">Charger plus de commentaires</div>');
    }
  });
}

async function updateComment(commentID)
{
  var comment = $('.comment[data-commentID="'+ commentID +'"]');

  const { value: message } = await Swal.fire({
    title: 'Votre commentaire',
    input: 'textarea',
    inputValue: comment.attr('data-originalComment')
  });

  if (message)
  {
    $.getJSON(ajaxURL+'cms_updateComment', {
      commentID: commentID,
      message: message
    }, function(json) {
      if (json.error)
      {
        showError(json.data.message);
      }
      else
      {
        showSuccess(json.data.message);
        comment.attr('data-originalComment', message);
        comment.find('> .content > .message').html(json.data.comment);
      }
    });
  }
}

async function deleteComment(commentID)
{
  var comment = $('.comment[data-commentID="'+ commentID +'"]');

  Swal.fire({
    title: 'Confirmation',
    text: 'Êtes-vous sûr de vouloir supprimer ce commentaire ?',
    type: 'warning',
    showCancelButton: true,
  }).then((result) => {
    if (result.value)
    {
      $.getJSON(ajaxURL+'cms_deleteComment', {
        commentID: commentID
      }, function(json) {
        if (json.error)
        {
          showError(json.data.message);
        }
        else
        {
          showSuccess(json.data.message);
          comment.hide('slow')
        }
      });
    }
  });
}
$(function() {
  $('.module-slider').each(function() {
    $(this).owlCarousel({
			loop: true,
      items: 1,
      autoHeight: true,
			nav: parseInt($(this).attr('data-nav')),
			navText: ['<i class="fa fa-chevron-left" aria-hidden="true"></i>', '<i class="fa fa-chevron-right" aria-hidden="true"></i>'],
			dots: parseInt($(this).attr('data-dots')),
			lazyLoad: true,
			autoplay: parseInt($(this).attr('data-autoplay')),
			autoplayTimeout: parseInt($(this).attr('data-speed')),
			autoplayHoverPause: false
		});
  });
});
$(function() {
  $('.sub-pages-layout-5 .owl-carousel').each(function() {
		var subpagesSlider = $(this);

		subpagesSlider.on('initialized.owl.carousel changed.owl.carousel', function(property) {
			if ($(document).width() < 640)
				return;

			var current = property.item.index;
			var prev = $(property.target).find(".owl-item").eq(current).prev();
			var next = $(property.target).find(".owl-item").eq(current).next();

			subpagesSlider.find(".owl-item").removeClass('previous-item next-item');
			prev.addClass('previous-item');
			next.addClass('next-item');
		});

    subpagesSlider.owlCarousel({
			loop: true,
      items: 1,
      autoHeight: true,
			nav: true,
			navText: ['<span></span>', '<span></span>'],
			dots: true,
			lazyLoad: true,
			responsive: {
        0: {
          stagePadding: 0
        },
        640: {
					stagePadding: 100
				}
			}
		});
  });
});
$(function() {
  if (typeof mapData !== 'undefined' && mapData.length > 0)
  {
    for (var i = 0; i < mapData.length; i++)
    {
      var data = mapData[i];
      var map = L.map('map_'+ data.id).setView(data.coordinates, data.zoom);

      L.tileLayer(data.tilesUrl, {
        attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/" target="_blank" rel="noopener noreferrer">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank" rel="noopener noreferrer">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/" target="_blank" rel="noopener noreferrer">Mapbox</a>',
        maxZoom: 18,
        id: 'mapbox.streets',
        accessToken: data.accessToken
      }).addTo(map);
    
      // Markers
      for (var i = 0; i < data.markers.length; i++)
      {
        var marker = L.marker(data.markers[i].coordinates).addTo(map);

        if (data.markers[i].content)
        {
          marker.bindPopup(data.markers[i].content);

          if (data.markers[i].isOpen)
            marker.openPopup();
        }
      }

      // Area
      if (data.area)
      {
        var circle = L.circle(data.coordinates, {
          color: 'red',
          fillColor: '#f03',
          fillOpacity: 0.3,
          radius: data.areaDistance
        }).addTo(map);
      }
    }
  }
});
var contactFormRenderInstances = {};

$(function() {
	
	$('.contact-form-container').each(function() {
		var id = $(this).parents('.bloc-container').attr('data-blocID');
		var originalFormFields = JSON.parse($(this).find('.original-form-fields').text());

		contactFormRenderInstances[id] = $(this).find('.form-render').formRender({
			dataType: 'json',
			i18n: {
				location: 'https://formbuilder.online/assets/lang/'
			},
			formData: JSON.stringify(originalFormFields)
		});
	});

	$('form.contact-form-container').submit(function() {
		var id = $(this).parents('.bloc-container').attr('data-blocID');
		var field = $(this).find('input[name="formFields"]');
		var data = contactFormRenderInstances[id].userData;
		saveContactFormDataJSON(field, data);
		return true;
	});
});

function saveContactFormDataJSON(field, data) {
	field.val(JSON.stringify(data));
} // saveContactFormDataJSON()
$(function () {
    changeButtonLinkType();

    $('#field_buttonLinkType').change(function () {
        changeButtonLinkType();
    });
});

$(function () {
    $('#field_buttonStyle').change(function () {
        if ($(this).val() === 'custom') {
            $('.button-style-custom').show();
        } else {
            $('.button-style-custom').hide();
        }
    });
    $('#field_buttonStyle').change();
});

function changeButtonLinkType() {
    const value = $('#field_buttonLinkType').val();

    $('div[data-button-link-type]').hide();
    $('div[data-button-link-type="' + value + '"]').show();

    if (value !== 'page') {
        $('#field_buttonPageID').val('');
    }

    if (value !== 'url') {
        $('#field_buttonURL').val('');
    }

    if (value !== 'file') {
        $('#field_buttonFile').val('');
    }
}

$(function () {
    $('#field_pictureLinkType').change(function () {
        changePictureLinkType();
    });

    changePictureLinkType();
});

function changePictureLinkType() {
    const value = $('#field_pictureLinkType').val();

    $('div[data-picture-link-type]').hide();
    $('div[data-picture-link-type="' + value + '"]').show();

    if (value !== 'page') {
        $('#field_internLink').val('');
    }

    if (value !== 'url') {
        $('div[data-picture-link-type="url"] input').val('');
    }

    if (value !== 'file') {
        $('#field_fileLink').val('');
    }
}

$(window).resize(function() {
  $('.icon .block').each(function() {
    resizeIcon($(this));
  });
});

function resizeIcon(item)
{
  var blockWidth = item.width();
  var fontSize = blockWidth * 0.5;

  item.find('.fa').css({
    'font-size': fontSize +'px',
    'line-height': fontSize +'px'
  });
} // resizeIcon()
$(window).on('load', function() {
  $('.fullscreen-block').each(function() {
    resizeFullscreenPictureBlock($(this));
  });
});

$(window).resize(function() {
  $('.fullscreen-block').each(function() {
    resizeFullscreenPictureBlock($(this));
  });
});

function resizeFullscreenPictureBlock(item)
{
  item.css('left', 'auto');
  var offset = item.offset();
	item.css({
    position: 'relative',
    left: -offset.left
  });
}
$(function() {

  if ($('input[name="websiteType"]:checked').val() == 1)
    $('input[name="websiteType"][value="1"]').click();

  $('.ornaweb-website-form').on('click', 'input[name="websiteType"]', function() {
    var type = $(this).val();

    $('.ornaweb-website-form .website-creation-type').not('[data-type="'+ type +'"]').fadeOut('slow', function() {
      $('.ornaweb-website-form .website-creation-type[data-type="'+ type +'"]').fadeIn();
    });
  });

  $('#generatePassword').click(function(e) {
    e.preventDefault();

    var password		= '';
    var passwordLength	= 8;
    var characters		= "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    for (var i = 0 ; i < passwordLength ; i++)
      password += characters[parseInt(Math.random() * characters.length)];

    $('#field_userPassword1, #field_userPassword2').val(password);
    $('.toggle-password-field').not('.password-visible').click();
  });

  $(document).on('lity:ready', function(event, instance) {
    var element = instance.element();
    var opener = instance.opener();
    var isTemplatePreview = opener.hasClass('template-preview');

    if (isTemplatePreview)
    {
      element.find('.lity-container').css({
        'width': 'calc(100% - 40px)',
        'max-width': '1600px'
      });
    }
  });

  $('.ornaweb-website-form').on('click', '.go-to-step', function(e) {
    e.preventDefault();

    var step = $(this).attr('data-step');
    gotoWebsiteBuilderStep(step);
  });
  
  $('.ornaweb-website-form').on('click', '.select-template', function(e) {
    e.preventDefault();
    var template = $(this).parents('.template');
    $('.ornaweb-website-form').find('input[name="websiteTemplate"]').val(template.attr('data-template-id'));

    gotoWebsiteBuilderStep(2);
  });

  $('.ornaweb-website-form').on('submit', 'form', function(e) {
    // Check step 2 required fields
    return checkStep2();
  });

  $('#field_websiteName').keyup(delay(function (e) {
    fieldVerification('name');
  }, 500));

  $('#field_userName').keyup(delay(function (e) {
    fieldVerification('username');
  }, 500));

  $('#field_userMail').keyup(delay(function (e) {
    fieldVerification('email');
  }, 500));

  $('#field_userPassword1, #field_userPassword2').keyup(delay(function (e) {
    fieldVerification('password');
  }, 500));
});

function checkStep2(silent = false, skipEmptyFields = false)
{
  if (!silent)
  {
    $('#createWebsiteSubmit').prop('disabled', true);
    $('.website-creation-waiting').show();
    scrollToAnchor('#websiteCreationAnchor', 500, 200);
  }

  $('#field_userName, #field_userMail, #field_userPassword1, #field_userPassword2').removeClass('error');
  $('.error-info').hide();

  var errorList = [];

  if (error = fieldVerification('username', skipEmptyFields))
    errorList.push(error);
    
  if (error = fieldVerification('email', skipEmptyFields))
    errorList.push(error);
    
  if (error = fieldVerification('password', skipEmptyFields))
    errorList.push(error);
    
  if (error = fieldVerification('cgu', skipEmptyFields))
    errorList.push(error);

  if (errorList.length)
  {
    if (!silent)
      Swal.fire('Votre formulaire contient des erreurs', errorList.join('<br>'), 'error');

    $('#createWebsiteSubmit').prop('disabled', false);
    $('.website-creation-waiting').hide();
    return false;
  }

  return true;
} // checkStep2()

function fieldVerification(name, skipEmptyFields = false)
{
  if (name == 'name')
  {
    var field = $('#field_websiteName');
    var error = checkWebsiteNameField(skipEmptyFields);
  }
  else if (name == 'username')
  {
    var field = $('#field_userName');
    var error = checkUsernameField(skipEmptyFields);
  }
  else if (name == 'email')
  {
    var field = $('#field_userMail');
    var error = checkEmailField(skipEmptyFields);
  }
  else if (name == 'password')
  {
    var field = $('#field_userPassword1, #field_userPassword2');
    var error = checkPasswordField(skipEmptyFields);
  }
  else if (name == 'cgu')
  {
    var field = $('#field_cgu');
    var error = checkCguField(skipEmptyFields);
  }

  if (!error)
  {
    field.removeClass('error');
    field.parents('.form-group').find('.error-info').hide();
  }
  
  return error;
} // fieldVerification()

function checkWebsiteNameField(skipEmptyFields = false)
{
  var error = '';
  var value = $('#field_websiteName').val();

  if (!value)
  {
    if (skipEmptyFields)
      return '';

    error = 'Veuillez entrer le nom de votre site internet.';
  }

  if (error)
    displayFieldError($('#field_websiteName'), error);

  return error;
}

function checkUsernameField(skipEmptyFields = false)
{
  var error = '';
  var value = $('#field_userName').val();
  var regex = /[^a-zA-Z0-9\-._ ]/gi;
  usernameInvalid = regex.exec(value);

  if (!value)
  {
    if (skipEmptyFields)
      return '';

    error = 'Veuillez entrer votre nom d\'utilisateur.';
  }
  else if (value.length < 2 || value.length > 20)
  {
    error = 'Votre nom d\'utilisateur doit faire entre 2 et 20 caractères.';
  }
  else if (usernameInvalid !== null)
  {
    error = 'Le nom d\'utilisateur ne peut contenir que des caractères alphanumériques ou les caractères suivants : tirets, underscores, points ou espaces.';
  }

  if (error)
    displayFieldError($('#field_userName'), error);

  return error;
} // checkUsername()

function checkEmailField(skipEmptyFields = false)
{
  var error = '';
  var value = $('#field_userMail').val();

  if (!value)
  {
    if (skipEmptyFields)
      return '';

    error = 'Veuillez entrer votre adresse e-mail.';
  }
  else if (!checkEmail(value))
  {
    error = 'Veuillez saisir une adresse e-mail valide.';
  }

  if (error)
    displayFieldError($('#field_userMail'), error);

  return error;
} // checkEmail()

function checkPasswordField(skipEmptyFields = false)
{
  var error = '';
  var value1 = $('#field_userPassword1').val();
  var value2 = $('#field_userPassword2').val();

  if (!value1 || !value2)
  {
    if (skipEmptyFields)
      return '';

    error = 'Veuillez entrer un mot de passe.';
  }
  else if (value1 != value2)
  {
    error = 'Les mots de passe ne correspondent pas.';
  }
  else if (value1.length < 6)
  {
    error = 'Le mot de passe doit faire au minimum 6 caractères.';
  }

  if (error)
    displayFieldError($('#field_userPassword1, #field_userPassword2'), error);

  return error;
} // checkPassword()

function checkCguField(skipEmptyFields = false)
{
  if (skipEmptyFields)
    return '';
    
  var error = '';

  if (!$('#field_cgu').is(':checked'))
    error = 'Vous devez accepter les conditions générales d\'utilisation.';

  if (error)
    displayFieldError($('#field_cgu'), error);

  return error;
} // checkCguField()

function displayFieldError(field, error)
{
  field.addClass('error');
  field.parents('.form-group, .checkbox').find('.error-info').html('<i class="fa fa-exclamation-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="'+ error +'"></i>');
  field.parents('.form-group, .checkbox').find('.error-info').show();
} // displayFieldError()

function gotoWebsiteBuilderStep(step)
{
  // Form verification
  if (step == 2)
  {
    var errorList = [];
    var websiteType = $('input[name="websiteType"]:checked').val();

    // Check fields on step 1
    if (error = fieldVerification('name', false))
      errorList.push(error);
    
    if (!websiteType)
    {
      var error = 'Veuillez sélectionner une méthode de création.';
      displayFieldError($('input[name="websiteType"]'), error)
      errorList.push(error);
    }

    if (websiteType == 1 && !$('#field_websiteTemplate').val())
      errorList.push('Veuillez sélectionner le modèle pour votre site internet.');

    if (errorList.length)
    {
      Swal.fire('Votre formulaire contient des erreurs', errorList.join('<br>'), 'error');
      return false;
    }

    /* Check fields on step 2 if user has already gone to this step previously:
     * form sent to server but with errors so fields aren't empty. */
    checkStep2(true, true);
    
    if (websiteType == 2)
      $('.company-services').show();
    else
      $('.company-services').hide();
  }

  $('.ornaweb-website-form .step').not('[data-step="'+ step +'"]').fadeOut('slow', function() {
    $('.ornaweb-website-form .step[data-step="'+ step +'"]').fadeIn();
  });
} // gotoWebsiteBuilderStep()
$(function() {
	animateProgressbar1();
	animateProgressbar2();
});

$(window).scroll(function() {
	animateProgressbar1();
	animateProgressbar2();
});

function animateProgressbar1()
{
	var speed = 4000;

	$('.progressbar-layout-1').not('.animated').each(function() {
		var item = $(this);
		var value = item.find('.progressbar-progress').attr('data-value');

		if ($(this).isInViewport())
		{
			item.find('.progressbar-progress').animate({
				width: value + '%'
			}, speed);
			item.find('.value').animate({
				left: value + '%'
			}, speed);

			// Counter
		  $({ Counter: 0 }).animate({ Counter: value }, {
		    duration: speed,
		    easing: 'swing',
		    step: function () {
		      item.find('.value span').text(Math.ceil(this.Counter));
		    }
		  });

			item.addClass('animated');
		}
  });
}

function animateProgressbar2()
{
	var speed = 4000;

	$('.progressbar-layout-2, .progressbar-layout-3, .progressbar-layout-4').not('.animated').each(function() {
		var item = $(this);
		var value = item.find('.progressbar-progress').attr('data-value');

		if ($(this).isInViewport())
		{
			item.find('.progressbar-progress').animate({
				width: value + '%'
			}, speed);

			// Counter
		  $({ Counter: 0 }).animate({ Counter: value }, {
		    duration: speed,
		    easing: 'swing',
		    step: function () {
		      item.find('.value span').text(Math.ceil(this.Counter));
		    }
		  });

			item.addClass('animated');
		}
  });
}
$(function() {
	animateCounter();
});

$(window).scroll(function() {
	animateCounter();
});

function animateCounter()
{
	$('.counter').not('.animated').each(function() {
		var item = $(this);
		var value = item.attr('data-value');
		var speed = parseInt(item.attr('data-speed'));

		if ($(this).isInViewport())
		{
			// Counter
		  $({ Counter: 0 }).animate({ Counter: value }, {
		    duration: speed,
		    easing: 'swing',
		    step: function () {
		      item.find('.value .number').text(Math.ceil(this.Counter));
		    }
		  });

			item.addClass('animated');
		}
  });
} // animateCounter()
$(function() {
  $('.module-carousel').each(function() {
    $(this).owlCarousel({
			loop: true,
			nav: $(this).attr('data-nav'),
			navText: ['<i class="fa fa-chevron-left" aria-hidden="true"></i>', '<i class="fa fa-chevron-right" aria-hidden="true"></i>'],
			dots: $(this).attr('data-dots'),
			lazyLoad: true,
			autoplay: $(this).attr('data-autoplay'),
			autoplayTimeout: $(this).attr('data-speed'),
			autoplayHoverPause: false,
      responsive: {
        0: {
          items: $(this).attr('data-mobile-items'),
          margin: 20
        },
        728: {
          items: $(this).attr('data-tablet-items'),
          margin: 30
        },
        1024: {
          items: $(this).attr('data-desktop-items'),
          margin: 40
        }
      }
		});
  });
});
$(window).resize(function() {
  $('.list-icons .block').each(function() {
    resizeListIcon($(this));
  });
});

function resizeListIcon(item)
{
  var blockWidth = item.width();
  var fontSize = blockWidth * 0.5;

  item.find('.fa').css({
    'font-size': fontSize +'px',
    'line-height': fontSize +'px'
  });
} // resizeListIcon()
const rocket = document.querySelector('.ornaweb-homepage-picture .item-rocket');
const ornawebHomepagePictureRandomX = ornawebHomepagePictureRandom(2, 8);
const ornawebHomepagePictureRandomY = ornawebHomepagePictureRandom(-10, 25);
const ornawebHomepagePictureRandomDelay = ornawebHomepagePictureRandom(0, 1);
const ornawebHomepagePictureRandomTime = ornawebHomepagePictureRandom(3, 6);
const ornawebHomepagePictureRandomTime2 = ornawebHomepagePictureRandom(5, 10);
const ornawebHomepagePictureRandomAngle = ornawebHomepagePictureRandom(2, 4);

if (rocket !== null) {
    TweenLite.set(rocket, {
        x: ornawebHomepagePictureRandomX(-1),
        y: ornawebHomepagePictureRandomX(1),
        rotation: ornawebHomepagePictureRandomAngle(-1)
    });

    ornawebHomepagePictureMoveX(rocket, 1);
    ornawebHomepagePictureMoveY(rocket, -1);
    ornawebHomepagePictureRotate(rocket, 1);
}

function ornawebHomepagePictureRotate(target, direction) {

    TweenLite.to(target, ornawebHomepagePictureRandomTime2(), {
        rotation: ornawebHomepagePictureRandomAngle(direction),
        delay: ornawebHomepagePictureRandomDelay(),
        ease: Sine.easeInOut,
        onComplete: ornawebHomepagePictureRotate,
        onCompleteParams: [target, direction * -1]
    });
}

function ornawebHomepagePictureMoveX(target, direction) {

    TweenLite.to(target, ornawebHomepagePictureRandomTime(), {
        x: ornawebHomepagePictureRandomX(direction),
        ease: Sine.easeInOut,
        onComplete: ornawebHomepagePictureMoveX,
        onCompleteParams: [target, direction * -1]
    });
}

function ornawebHomepagePictureMoveY(target, direction) {

    TweenLite.to(target, ornawebHomepagePictureRandomTime(), {
        y: ornawebHomepagePictureRandomY(direction),
        ease: Sine.easeInOut,
        onComplete: ornawebHomepagePictureMoveY,
        onCompleteParams: [target, direction * -1]
    });
}

function ornawebHomepagePictureRandom(min, max) {
    const delta = max - min;
    return (direction = 1) => (min + delta * Math.random()) * direction;
}
$(function() {
  $('.module-ornaweb-homepage-slider').each(function() {
    $(this).owlCarousel({
			loop: true,
      items: 1,
      autoHeight: true,
			nav: parseInt($(this).attr('data-nav')),
			navText: ['<span></span>', '<span></span>'],
			dots: parseInt($(this).attr('data-dots')),
			lazyLoad: true,
			autoplay: parseInt($(this).attr('data-autoplay')),
			autoplayTimeout: parseInt($(this).attr('data-speed')),
			autoplayHoverPause: false
		});
  });
});
// Sticky header
jQuery(function() {
  stickyHeader();
});

jQuery(window).scroll(function() {
  stickyHeader();
});

function stickyHeader()
{
  if (jQuery(window).scrollTop())
    jQuery('body').addClass('sticky-header');
  else
    jQuery('body').removeClass('sticky-header');
}
$(window).on('load', function() {
  $("img.unveil, img.lazyload").unveil(200, function() {
    $(this).addClass('loaded');
    $(this).on('load', function() {
      if ($(this).parents().hasClass('resizePicture')) {
        // if parent has resizePicture class, launch resizing after load.
        resizePicture($(this).parent());
      }
    });
  });
});
$(function() {
    $('body').tooltip({
        selector: '[data-toggle="tooltip"]'
    });

    $('[data-toggle="tooltip"]').on('show.bs.tooltip', function() {
        let $tooltip = $($(this).data('bs.tooltip').tip);
        let customClass = $(this).attr('data-tooltip-css-class');

        if (customClass !== undefined) {
            $tooltip.addClass(customClass);
        }
    });
});
$(function() {
	$('table').stickyTableHeaders({fixedOffset: stickyHeaderMargin});
});
$(function() {
  $(document).on('click', '.lightbox', lity);
});
FontAwesomeConfig = { autoReplaceSvg: 'nest' }