/*
vp functions

CREATION : 150205

NOTE : This script is loaded by the last_version_file() php func.

HISTORY : 
        • 241206 : delCookie(); num();
        • 241031 : Vannilla helpers 
                    - hide()
                    - show()
                    - toggle()
        • 240917: 
            - randArrayItem(ar)
        • 240916 v02.06 (always v02.05)
            - copyText(element_id)
        • 240524 v02.06 (always v02.05)
            - vpTransition()
            - vpPlopFontSize()
            - vpEventListen()
            - vpEventDispatch()
            - VpProgress()
            - random_seed()
            - shuffle_seed()

        • 180626 v02.05 : add sortBy2Properties() and modif of sortByProperty();
        • 160330 v02.03 : add hideAfterDelay(jQueryObject, delay); 
        • 160309 v02.02 : add : sortByProperty();

*/


// Touch device or not
function is_touch_device() {
	return (('ontouchstart' in window)
		|| (navigator.MaxTouchPoints > 0)
		|| (navigator.msMaxTouchPoints > 0));
} 
IS_TOUCH_DEVICE = is_touch_device();

// For retina.
IS_RETINA = window.devicePixelRatio == 2;


//-------------------------------------------------------------------
//-------------------------------------------------------------------
/* IS MOBILE
*/
// for login, name...
function isMobile(){
	var check = false;
	(function(a){if(/(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.test(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.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
	return check;
	
}

IS_MOBILE = isMobile();

// Browser detection
//-----------------------------------
BROWSER = {
	chrome: false,
	mozilla: false,
	opera: false,
    msieOld: false, // ie before v10
    msie: false,
    safari: false
 };
 var sUsrAg = navigator.userAgent;
 if(sUsrAg.indexOf("Chrome") > -1) {
 	BROWSER.chrome = true;
 } else if (sUsrAg.indexOf("Safari") > -1) {
 	BROWSER.safari = true;
 } else if (sUsrAg.indexOf("Opera") > -1) {
 	BROWSER.opera = true;
 } else if (sUsrAg.indexOf("Firefox") > -1) {
 	BROWSER.mozilla = true;
 } else if (sUsrAg.indexOf("MSIE") > -1) {
 	BROWSER.msieOld = true;
 } else if (!(window.ActiveXObject) && "ActiveXObject" in window) {
 	BROWSER.msie = true;
 }

/**
 * Determine the mobile operating system.
 * This function returns one of 'iOS', 'Android', 'Windows Phone', or 'unknown'.
 *
 * @returns {String}
 */
 function getMobileOperatingSystem() {
 	var userAgent = navigator.userAgent || navigator.vendor || window.opera;

      // Windows Phone must come first because its UA also contains "Android"
 	if (/windows phone/i.test(userAgent)) {
 		return "Windows Phone";
 	}

 	if (/android/i.test(userAgent)) {
 		return "Android";
 	}

    // iOS detection from: http://stackoverflow.com/a/9039885/177710
 	if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
 		return "iOS";
 	}

 	return "unknown";
 }


/**
* For the count of the properties of an object
*/
 function countProperties(obj) {
 	var count = 0;

 	for(var prop in obj) {
 		if(obj.hasOwnProperty(prop))
 			++count;
 	}

 	return count;
 }



// THE DEBUGGER
//----------------------------------------------
 function debug(x){
 	document.getElementById('debug').innerHTML = x + "<br>" + document.getElementById('debug').innerHTML;
 	document.getElementById('debug').style.display='block';
 }
 function debugp(x){
 	document.getElementById('debug').innerHTML = x;
 	document.getElementById('debug').style.display='block';
 }
 function debugReset(){
 	document.getElementById('debug').innerHTML = "";
 }


//--------------------------------------------------------------------
// No action but needed for vpcms
 function wait_ajax(){}

//--------------------------------------------------------------------
// simply reload the page. problem if href ends with '#'
 function reloadPage(){ return reload_page();}
 function reload_page(){
 	var url;
 	var href = document.location.href;
 	if (href.substr(-1, 1) == "#"){ url = href.substr(0, -1) }else{ url = href; } 
 	document.location = url;
 }
//--------------------------------------------------------------------
// simply go to the desired page.
// gotoPage
 function goToPage(page){ return go2page(page) } 
 function go2Page(page){ return go2page(page) }
 function go2page(page){
 	document.location = page;
 }
//--------------------------------------------------------------------
// simply go to the desired page.
 function go2pageget(page, var_name, var_value){
 	document.location = page+"?"+var_name+"="+var_value;
 }
//--------------------------------------------------------------------
// simply go to the desired page.
 function openBlankPage(page){
 	window.open(page);
 }
//--------------------------------------------------------------------
// Go to previous page
 function go2Previous(){
 	window.history.go(-1);
 }

//--------------------------------------------------------------------
//--------------------------------------------------------------------
// 
/**
 * Emule the php trim function
 * @param  {[type]} str [description]
 * @return {[type]}     [description]
 */
 function trim(str){
 	if (typeof(str) == "string"){
 		return str.replace(/^\s+|\s+$/g, '') ;
 	}
 }

 function str_replace(search, replace, subject) {
    // Replace all occurrences of the search string with the replacement string
    // 
    // +    discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_str_replace/
    // +       version: 812.1017
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Gabriel Paderni
    // +   improved by: Philip Peterson
    // +   improved by: Simon Willison (http://simonwillison.net)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   bugfixed by: Anton Ongson
    // +      input by: Onno Marsman
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +    tweaked by: Onno Marsman
    // *     example 1: str_replace(' ', '.', 'Kevin van Zonneveld');
    // *     returns 1: 'Kevin.van.Zonneveld'
    // *     example 2: str_replace(['{name}', 'l'], ['hello', 'm'], '{name}, lars');
    // *     returns 2: 'hemmo, mars'

 	var f = search, r = replace, s = subject;
 	var ra = r instanceof Array, sa = s instanceof Array, f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;

 	while (j = 0, i--) {
 		if (s[i]) {
 			while (s[i] = (s[i]+'').split(f[j]).join(ra ? r[j] || "" : r[0]), ++j in f){};
 		}
 	};

 	return sa ? s : s[0];
 }

 function nl2br (str, is_xhtml) {
  // http://kevin.vanzonneveld.net
  // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // +   improved by: Philip Peterson
  // +   improved by: Onno Marsman
  // +   improved by: Atli Þór
  // +   bugfixed by: Onno Marsman
  // +      input by: Brett Zamir (http://brett-zamir.me)
  // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  // +   improved by: Brett Zamir (http://brett-zamir.me)
  // +   improved by: Maximusya
  // *     example 1: nl2br('Kevin\nvan\nZonneveld');
  // *     returns 1: 'Kevin<br />\nvan<br />\nZonneveld'
  // *     example 2: nl2br("\nOne\nTwo\n\nThree\n", false);
  // *     returns 2: '<br>\nOne<br>\nTwo<br>\n<br>\nThree<br>\n'
  // *     example 3: nl2br("\nOne\nTwo\n\nThree\n", true);
  // *     returns 3: '<br />\nOne<br />\nTwo<br />\n<br />\nThree<br />\n'
  var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br ' + '/>' : '<br>'; // Adjust comment to avoid issue on phpjs.org display

  return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
}

// {{{ isset
function isset(  ) {
    // Determine whether a variable is set
    // 
    // +    discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_isset/
    // +       version: 809.522
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: FremyCompany
    // +   improved by: Onno Marsman
    // *     example 1: isset( undefined, true);
    // *     returns 1: false
    // *     example 2: isset( 'Kevin van Zonneveld' );
    // *     returns 2: true
	
	var a=arguments; var l=a.length; var i=0;
	
	if (l==0) { 
		throw new Error('Empty isset'); 
	}
	
	while (i!=l) {
		if (typeof(a[i])=='undefined' || a[i]===null) { 
			return false; 
		} else { 
			i++; 
		}
	}
	return true;
}// }}}

/**
 * Verify the mail validity
 * @param  {[type]} email [description]
 * @return {[type]}       [description]
 */
function verif_mail(email){
	var arobase=email.indexOf("@");
	var point= email.lastIndexOf(".");
	if((arobase < 3)||(point + 2 > email.length) ||(point < arobase+3)){
		return false;
	}else{
		return true;
	}
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
// secure some caracters
//-------------------------------------------------------------------
//-------------------------------------------------------------------
function secure_string_for_ajax(str){
	
	var reg=new RegExp("(&)", "g");
	str = str.replace(reg,'|#and#|');
	
	var reg=new RegExp("(\\+)", "g");
	str = str.replace(reg,'|#plus#|');

	
	return str;
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
/* INPUT SANITIZE
USE : input_sanitize(input_id);
*/
// for login, name...
function str_sanitize(str){
	return str.replace(/[|&;$"<>{}\[\]()+,]/g, "");
}
// For textarea
function str_sanitize_light(str){
	return str.replace(/[|&$"<>{}\[\]]/g, "");
}
function input_sanitize(el_id, option){
	option = typeof option !== 'undefined' ? option : "";
	$("#"+el_id).keyup(function() {
		if(option == "") $(this).val(str_sanitize($(this).val()));
		if(option == "light") $(this).val(str_sanitize_light($(this).val()));
	});
	$("#"+el_id).blur(function() {
		if(option == "") $(this).val(str_sanitize($(this).val()));
		if(option == "light") $(this).val(str_sanitize_light($(this).val()));
	});
}


//--------------------------------------------------------------------
// Replace the html code of the h1 by the str
function replaceTitle(str){
	$("h1").html(str);
}


// send with POST the var
// http://stackoverflow.com/questions/133925/javascript-post-request-like-a-form-submit
// CALL JS : post("an/url.php", {"game_id":2, "class_id":31});
// CALL PHP : $link = 'javascript:post("'.page_url_from_special("admin:game_preview").'", {"game_id":'.$aGamesSelected[$i]['id'].', "class_id":'.$class_id.'});';
//-----------------------------------
function post(path, params, method) {
    method = method || "post"; // Set method to post by default if not specified.

    // The rest of this code assumes you are not using a library.
    // It can be made less wordy if you use one.
    var form = document.createElement("form");
    form.setAttribute("method", method);
    form.setAttribute("action", path);

    for(var key in params) {
    	if(params.hasOwnProperty(key)) {
    		var hiddenField = document.createElement("input");
    		hiddenField.setAttribute("type", "hidden");
    		hiddenField.setAttribute("name", key);
    		hiddenField.setAttribute("value", params[key]);

    		form.appendChild(hiddenField);
    	}
    }

    document.body.appendChild(form);
    form.submit();
 }

/**
 * Call a php script in ajax that will set session vars and then go to the desired page.
 * @param {[type]} url    [description]
 * @param {Object} params Ex : {"game_id":2, "class_id":31}
 * // CALL JS : setSessionAndGoToPage("an/url.php", {"game_id":2, "class_id":31});
// CALL PHP : $link = 'javascript:setSessionAndGoToPage("'.page_url_from_special("admin:game_preview").'", {"game_id":'.$aGamesSelected[$i]['id'].', "class_id":'.$class_id.'});';
 */
 function setSessionAndGoToPage(url, params, path){
    // default
 	if (!isset(path)) path = "";
 	var content_result = document.createElement("div");
 	content_result.setAttribute("id", "setSession_content_result");
 	document.body.appendChild(content_result);

    /// SECURITY in PHP  ///

 	var getStr = "?";
 	for(var key in params) {
 		getStr += key+"="+params[key]+"&";
 	}
 	getStr.slice(0, - 1);
 	$("#setSession_content_result").load(path+"plugins/vp_set_session_var/set_session_var.php"+getStr, function(){
 		go2page(url);
 	});

 }

/**
 * [capitalizeFirstLetter description]
 * @param  {[type]} string [description]
 * @return {[type]}        [description]
 */
 function capitalizeFirstLetter(string) {
 	return string.charAt(0).toUpperCase() + string.slice(1);
 }

/**
 * Center a DOM at the center of another.
 * CAUTION : 
 * @param  {[type]} e The element to be centered
 * @param  {[type]} p The base element
 * @return {[type]}   [description]
 * USAGE : 
    function centerHandCircles(){
        $(".answer_unit > .hand_circle").each(function(){
            centerToElement($(this), $(this).parent());
        });
    }
    window.onresize = function(event) {
        centerHandCircles();
    }
    centerHandCircles();
 */
 function centerToElement(e, p){
 	var p = e.parent();
 	var pos = p.offset();
 	e.css("position", "absolute");
 	e.css("left", (pos.left + p.outerWidth()/2) - (e.width()/2));
 	e.css("top", (pos.top + p.outerHeight()/2) - (e.height()/2));

 }

/**
 * To avoid the page scroll when the max scroll of a div is reached.
 * AUTHOR : vp
 * 
 */
 $(function(){
 	$(".scrollable").scroll(function(){
 		var maxScrollTop = $(this)[0].scrollHeight - $(this).outerHeight();
 		if($(this).scrollTop() == maxScrollTop){
 			$(this).scrollTop(maxScrollTop - 1);
 		}
 	});
 });

/**
 * To add to jQuery the function focusTextToEnd();
 * @param  {[type]} $ [description]
 * @return {[type]}   [description]
 */
 (function($){
 	$.fn.focusTextToEnd = function(){
 		this.focus();
 		var $thisVal = this.val();
 		this.val('').val($thisVal);
 		return this;
 	}
 }(jQuery));

/**
 * Format the input number.
 *
 * @param      {<number>}  n       { parameter_description }
 * @return     {<number>}  { description_of_the_return_value }
 */
 function num(n){
    // First we may have a string. in this case we try to convert it in number.
    //-----------------------------------
    if (!isNaN(n) && !isNaN(parseFloat(n))){
        // Then we convert it in number
        //-----------------------------------
        n = +n;
        // Then we format and return.
        //-----------------------------------
        const locale = navigator.language;  
        return n.toLocaleString(locale);
    // It's not a possible number, we simply return it.
     //----------------------------------- 
    }else{
        return n;
    }
 }

/**
 * [randInt description]
 * @return {[type]} [description]
 */
 function randInt(){
 	return Math.floor((Math.random() * 10000000) + 1);
 }

/**
 * Returns a number from 0 to the int
 * @param  {[type]} int [description]
 * @return {[type]}     [description]
 */
 function random(int){
 	return Math.floor((Math.random() * (int+1)));
 }

 /**
  * Returns a random number from min to max
  * @param  {[type]} int [description]
  * @return {[type]}     [description]
  */
 function rand(min, max){
 	return min + Math.floor((Math.random() * ((max-min)+1)));
 }

/**
 * Randomly returns an item from an array.
 *
 * @param      {<type>}  ar      array
 */
 function randArrayItem(ar){
    return ar[Math.floor(Math.random() * ar.length)];
 }


//-------------------------------------------------------------------
//-------------------------------------------------------------------
/* TEXTAREA char remaining (In function because some form are call in ajax and so are not there onload.
*/
 $(function(){
 	set_textarea_charsRemaining_action();
 	
 });
 function set_textarea_charsRemaining_action(){
 	$('textarea[maxlength]').keyup(function(){
 		
 		var max = parseInt($(this).attr('maxlength'));
 		if($(this).val().length > max){
 			$(this).val($(this).val().substr(0, $(this).attr('maxlength')));
 		}

 		$(this).parent().find('.charsRemaining').html('' + (max - $(this).val().length) + '');
 	});
 }



/**
 * [sortByProperty description]
 * @param  {ARRAY} ar       [description]
 * @param  {STRING} propName [description]
 * @param  {STRING} option [Can be 'asc' or 'desc']
 * @return {[type]}          [description]
 */
 function sortByProperty(ar, propName, options){
 	if (typeof options == "undefined") options = "asc";
 	if (options.includes('asc')) asc = true;
 	if (options.includes('desc')) asc = false;
 	ar.sort(function(a,b) {
 		if (asc){
 			return a[propName] > b[propName] ? 1 : -1;
 		}else{
 			return a[propName] < b[propName] ? 1 : -1;
 		}
 	});
 	return ar;
 }

/**
 * [sortBy2Properties description]
 * @param  {[type]} ar    [description]
 * @param  {[type]} prop1 [description]
 * @param  {[type]} prop2 [description]
 * @return {[type]}       [description]
 */
 function sortBy2Properties(ar, prop1, prop2, options){
 	if (typeof options == "undefined") options = "asc";
 	if (options.includes('asc')) asc = true;
 	if (options.includes('desc')) asc = false;

 	if (asc){
 		ar.sort(function (a, b){
 			if (a[prop1] < b[prop1]) {
 				return -1;
 			}
 			if (a[prop1] > b[prop1]) {
 				return 1;
 			}

 			if (a[prop1] == b[prop1]) {
 				if (a[prop2] < b[prop2]) {
 					return -1;
 				}
 				if (a[prop2] > b[prop2]) {
 					return 1;
 				}
 				return 0;
 			}  
 		});
 	}else{
 		ar.sort(function (a, b){
 			if (a[prop1] > b[prop1]) {
 				return -1;
 			}
 			if (a[prop1] < b[prop1]) {
 				return 1;
 			}

 			if (a[prop1] == b[prop1]) {
 				if (a[prop2] > b[prop2]) {
 					return -1;
 				}
 				if (a[prop2] < b[prop2]) {
 					return 1;
 				}
 				return 0;
 			}  
 		});
 	}

 	return ar;
 }

/**
 * [hideAfterDelay description]
 * @param  {[type]} dom   A jQuery object
 * @param  {[type]} delay Default 2000
 * @return {[type]}       [description]
 */
 function hideAfterDelay(jQueryObject, delay){
 	if (!isset(delay)) delay = 3000;
 	setTimeout(function() {
 		jQueryObject.fadeOut();
 	}, delay);

 }



/**
 * Replace the html content of the element by a waiter.
 * The waiter is displayed only after one second.
 * @param  {Dom element} element [description]
 * @param  {string} size  : "big[1-9]", "small[1-9]"
 * @return {void}         [description]
 */
 function display_waiter(element, size){
 	displayWaiter(element, size);
 }

 function displayWaiter(element, size){
 	if (isset(size) == false) size = "";
 	var rand = Math.round(Math.random()*10000);
 	var waiter_id = "waiter_"+rand;
 	element.html("<div class='waiter_container' style='display:none;' id='"+waiter_id+"'><i class='fa fa-spin fa-refresh waiter "+size+"'></i></div>");
 	element.delay(1000, "myQueue").queue("myQueue", function(){ 
 		var dom = $("#"+waiter_id);
 		if (dom.length){
 			dom.show();
 		}
 	}).dequeue("myQueue");

 }
 function hideWaiter(waiter_id){
 	
 }



/**
   * Decimal adjustment of a number.
   *
   * @param {String}  type  The type of adjustment.
   * @param {Number}  value The number.
   * @param {Integer} exp   The exponent (the 10 logarithm of the adjustment base).
   * @returns {Number} The adjusted value.
   */
 function decimalAdjust(type, value, exp) {
    // If the exp is undefined or zero...
 	if (typeof exp === 'undefined' || +exp === 0) {
 		return Math[type](value);
 	}
 	value = +value;
 	exp = +exp;
    // If the value is not a number or the exp is not an integer...
 	if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
 		return NaN;
 	}
    // Shift
 	value = value.toString().split('e');
 	value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
    // Shift back
 	value = value.toString().split('e');
 	return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
 }

  // Decimal round
  // EX : Math.round10(55.55, -1);   // 55.6
 if (!Math.round10) {
 	Math.round10 = function(value, exp) {
 		return decimalAdjust('round', value, exp);
 	};
 }
  // Decimal floor
 if (!Math.floor10) {
 	Math.floor10 = function(value, exp) {
 		return decimalAdjust('floor', value, exp);
 	};
 }
  // Decimal ceil
 if (!Math.ceil10) {
 	Math.ceil10 = function(value, exp) {
 		return decimalAdjust('ceil', value, exp);
 	};
 }



/**
 * HERE THE FORMER vpSITE.js
 */

 $(document).ready( function() {



    /*----------------------------/
    /* Plugins jQuery 
    /*---------------------------*/

    /**
     * Add the method cssValue() in jQuery.
     * @param  {[type]} $ : A jQuery object
     * @return {int}   : ex : 400px => 400
     */
 	(function($) {
 		$.fn.cssValue = function(p) {
 			return isNaN(parseFloat(this.css(p))) ? 0 : parseFloat(this.css(p));
 		};
 	})(jQuery);

 	

    /*----------------------------/
    /* NAVBAR
    /*---------------------------*/

 	if($('.navbar-fixed-top.shrinkable').length > 0) {
 		$('.wrapper').css('padding-top', 97);

 		$(window).scroll(function() {
 			if($(document).scrollTop() > 300) {
 				$('.navbar-fixed-top').addClass('shrink-active');
 			}else {
 				$('.navbar-fixed-top').removeClass('shrink-active');
 			}
 		});
 	}


    /*----------------------------/
    /* Height of main page : to fit the browser height
    /*---------------------------*/

 	var minHeight = $( window ).height() - ($(".page-header").height() + $("footer").height());
    if ($(".page-content.home").length == false){ // not on home page
        //$('.page-content').css("min-height", minHeight);
    }
    


    /*--------------------------------/
    /* MENU behaviour if mobile in game_page
    The main menu is hidden. A button in the h1 (right) display it on toggle.

    DEPRECATED in JGFR
    /*-------------------------------*/
    if (
        (IS_MOBILE || 1==1 ) // The menu has to be hidden even in desktop device.
        && $(".game_page").length > 0
        ){
    	
    	$("#main_navbar").fadeOut();
    $("#button_for_display_navbar").fadeIn();
    $("#button_for_display_navbar").click(function(){
    	$("#main_navbar").fadeToggle();
    });
    
 }else{
 	$("#button_for_display_navbar").hide();
 }
 


    /*--------------------------------/
    /* BOOTSTRAP POPOVER
    /*-------------------------------*/

    //-----------------------------------
    // The login popover
    //-----------------------------------
    // Try catch becaus we may load the functions script without the whole bootstrap things, whtich may would cause an error.
 try{
 	$('#login_popover_button').popover({
 		html: true,
 		title: '<i class="fa fa-user"></i> Login',
 		trigger: 'click',
 		placement:'bottom',
 		content: $("#login_assets_for_html_content").html()
 		
 	});
 } catch(err){}
    //-----------------------------------
    // The login / user popover (DEPRECATED)
    //-----------------------------------
    // $('#user_menu_popover_button').popover({
    //                     html: true,
    //                     title: '<i class="fa fa-user"></i> Login',
    //                     trigger: 'click',
    //                     placement:'bottom',
    //                     content: $("#user_menu_assets_for_html_content").html()
 
    //                 });


});


/**
 * COOKIE FUNCS
 *
 * */

/**
 * Set cookie
 * @param {[type]} cname  [description]
 * @param {[type]} cvalue [description]
 * @param {[type]} exdays [description]
 */
 function setCookie(cname, cvalue, exdays) {
 	if (typeof exdays === 'undefined') exdays = 1;
 	var d = new Date();
 	d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
 	var expires = "expires="+d.toUTCString();
 	document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
 }

 /**
  * [delCookie description]
  * @param  {[type]} cname [description]
  * @return {[type]}       [description]
  */
 function delCookie(cname){
     document.cookie = cname+"=;path=/;expires=" + new Date(0).toUTCString()
 }

 /**
 *  To get a cookie value
 *
 * To set a cookie : 
 * @param  {[type]} cname [description]
 * @return STRING       [description]
 *
 * CAUTION : a bool value will be get as string !! ("true" or "false")
 */
 function getCookie(cname) {
 	var name = cname + "=";
 	var ca = document.cookie.split(';');
 	for(var i = 0; i <ca.length; i++) {
 		var c = ca[i];
 		while (c.charAt(0) == ' ') {
 			c = c.substring(1);
 		}
 		if (c.indexOf(name) == 0) {
 			return c.substring(name.length, c.length);
 		}
 	}
 	return "";
 } 

/**
 * Because IE < 12 doen't have this function
 * @param  {[type]}  num [description]
 * @return {Boolean}     [description]
 */
 function isInteger(num) {
 	return (num ^ 0) === num;
 }


/**
 * Get the _GET param.
 * @param  {[type]} url [description]
 * @return {[type]}     [description]
 */
 function parseURLParams(url) {
 	var queryStart = url.indexOf("?") + 1,
 	queryEnd   = url.indexOf("#") + 1 || url.length + 1,
 	query = url.slice(queryStart, queryEnd - 1),
 	pairs = query.replace(/\+/g, " ").split("&"),
 	parms = {}, i, n, v, nv;

 	if (query === url || query === "") return;

 	for (i = 0; i < pairs.length; i++) {
 		nv = pairs[i].split("=", 2);
 		n = decodeURIComponent(nv[0]);
 		v = decodeURIComponent(nv[1]);

 		if (!parms.hasOwnProperty(n)) parms[n] = [];
 		parms[n].push(nv.length === 2 ? v : null);
 	}
 	return parms;
 }

/**
 * [dateFormat description]
 * @param  {jquery obj} o       [description]
 * @param  {options} options Can contain "date", "hours". Default "";
 * @return {STRING}         [description]
 *
 *
 * CAUTION : DOESN'T WORK.
 */
 function dateFormat(strDate, options){

 	console.log(strDate);
 	
    // Default
    //-----------------------------------
 	if(typeof options == "undefined"){
 		options = "";
 	}
    // default format
    //-----------------------------------
 	var format = "dd/MM/yyyy HH:mm:ss";

    // The options
    //-----------------------------------
 	var a = options.split(" ");

    // The window is draggable
    //-----------------------------------
 	if (a.indexOf('date') !== -1){
 		format = "dd/MM/yyyy";
 	}

 	if (a.indexOf('hours') !== -1){
 		format = "HH:mm:ss";
 	}

    // mod
    //-----------------------------------
 	return $.format.date(strDate, format);
   //  return DateFormat.format(strDate, format);

 }

/**
 * [hourMinutesFromTimestamp description]
 * @param  {[type]} ts A Date object (var d = new Date();)
 * @return {[type]}    [description]
 */
 function hourMinutesFromTimestamp(ts){
 	var date = new Date(ts);
 	var hours = date.getHours();
 	var minutes = "0" + date.getMinutes();
 	return hours + ':' + minutes.substr(-2);
 }
 function hmFromTimestamp(ts){ hourMinutesFromTimestamp(ts);}
 function hmsFromTimestamp(ts){
 	var date = new Date(ts);
 	var hours = date.getHours();
 	var minutes = "0" + date.getMinutes();
 	var seconds = "0" + date.getSeconds();
 	return hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
 }


/**
 * [scrollToElement description]
 * @param  {[type]} el    a jquery element
 * @param  {[type]} speed String / INT. Default 300
 * @param  {[type]} margin  To scroll with a margin (minus value will scroll a little less)
 * @return {[type]}       [description]
 */
 function scrollToElement(el, margin, speed, ease){
 	if (typeof speed === 'undefined') speed = 400;
 	if (typeof margin === 'undefined') margin = 0;
    if (typeof ease === 'undefined') ease = ""; // no ease because I don't load jqUI
    let p = el.offset();
    return scrollToPos(p.top + margin, speed, ease);
 }
/**
 * [scrollToPos description]
 * @param  INT pos   A position
 * @param  {[type]} speed [description]
 * @param  {[type]} ease  USELESS
 * @return {[type]}       [description]
 */
 function scrollToPos(pos, speed, ease){
 	if (typeof speed === 'undefined') speed = 400;
    if (typeof ease === 'undefined') ease = 'swing'; // no ease because I don't load jqUI
    $('html,body').animate({
    	scrollTop: pos
    }, speed, ease);
 }


/**
 * Used to load and execute javascript file. Suffers from same-domain restriction.
 * @param file JS file name
 * @param callback Subscribe to get notified when script file is loaded
 * Source : https://www.codeproject.com/Articles/5310336/Dynamically-Loading-a-JavaScript-File
 * USAGE : 
        requireXhr("js/remote.js", function () {
            sayHello("Hello");
          });
 **/
 function requireXhr(file, callback) {
  // object initialization
 	const xhr = new XMLHttpRequest();

  // subscribe to request events
 	xhr.onreadystatechange = function () {
    // readyState:
    // 0 UNSENT Client has been created. open() not called yet.
    // 1 OPENED open() has been called.
    // 2 HEADERS_RECEIVED send() has been called, and headers and status are available.
    // 3 LOADING Downloading; responseText holds partial data.
    // 4 DONE The operation is complete.

    // when not done, return
 		if (xhr.readyState !== 4) {
 			return;
 		}

    // done, check status code
    if (xhr.status !== 200) // 200 = OK
    {
    	return;
    }

    // now the file is loaded,
    // go and execute the script
    eval(xhr.responseText);

    // notify caller
    if (callback) {
    	callback();
    }
 };

  // open connection to file
 xhr.open("GET", file, true);

  // send the request
 xhr.send();
}


/**
 * [vpCountdown description]
 * @param  {[type]}   jqel     A jQuery element
 * @param  {[type]}   dateTo   An Epoch (timestamp in milis), 
 *                               or a date "YYYY-MM-DD" or "YYYY/MM/DD";
 * @param  {[type]}   options  {format:"DHMS" or "HMS" or "MS", 
 *                              lang:"Days/Hours/Minutes/Seconds" [strings separated by /],
 *                              play:BOOL [default true],
 *                              separator:STRING [default "/"],
 *                              utc:BOOL [default false], (now() will be utc time comparison)
 *                              }
 * @param  {Function} callback A anonymous function
 * @return {[type]}            [description]
 *
 * CALLER : 
 *
  vpCountdown(
        $("#countdown_in_alert_credit_count_reached"), 
        dateInCookie,
        {   format:"MS", 
            lang:"//minutes/secondes",
            play:true
        },
        function(){reload_page();}
    );
 */
function vpCountdown(jqel, dateTo, options, callback){

    // vars
    //-----------------------------------
	let str;

	let testIndex = 0;

    // Option management
    //-----------------------------------
	if(typeof options === 'undefined') options = {format:"DHMS", lang:"D/H/M/S", play:false};
	if(typeof options.format === 'undefined') options.format = "DHMS";
	if(typeof options.lang === 'undefined') options.lang = "D/H/M/S";
	if(typeof options.play === 'undefined') options.play = true;
	if(typeof options.separator === 'undefined') options.separator = "/";
	if(typeof options.utc === 'undefined') options.utc = false;

    // play
    //-----------------------------------
	let play = options.play;

    // format and lang
    //-----------------------------------
	let aFormat = options.format.split("");
	let aLang = options.lang.split(options.separator);
	let dString = typeof aLang[0] === 'undefined' ? "" : aLang[0];
	let hString = typeof aLang[1] === 'undefined' ? "" : aLang[1];
	let mString = typeof aLang[2] === 'undefined' ? "" : aLang[2];
	let sString = typeof aLang[3] === 'undefined' ? "" : aLang[3];


    // Set the date we're counting down to
	var countDownDate = new Date(dateTo).getTime();

    // Update the count down every 1 second
	var x = setInterval(function() {
		// str
		//-----------------------------------
		str = "";

      // Get today's date and time.
      // To avoid time zone issues, we may use utc time.
      //-----------------------------------
		let now
		if (options.utc == false){
			now = new Date().getTime();

		// To avoid time zone issues, the now() time must be utc
		//-----------------------------------
		}else{
			let nowTmp = new Date()
			let timezoneDiff = nowTmp.getTimezoneOffset() * 60 * 1000
			now = nowTmp.getTime() + timezoneDiff
		}


		// Find the distance between now and the count down date
		var distance = countDownDate - now;


		// Time calculations for days, hours, minutes and seconds
		var dNum = Math.floor(distance / (1000 * 60 * 60 * 24));
		var hNum = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
		var mNum = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
		var sNum = Math.floor((distance % (1000 * 60)) / 1000);

        // Display the result in the element with id="demo"
		if (aFormat.includes("D") && dNum > 0){
			str += `
			<div class='countdown_section'>
			<div class='countdown_num'>${dNum}</div>
			<div class='countdown_leg'>${dString}</div>
			</div>`;
		}
		if (aFormat.includes("H") && dNum > 0 && hNum > 0){
			str += `
			<div class='countdown_section'>
			<div class='countdown_num'>${hNum}</div>
			<div class='countdown_leg'>${hString}</div>
			</div>`;
		}
		if (aFormat.includes("M")){
			str += `
			<div class='countdown_section'>
			<div class='countdown_num'>${mNum}</div>
			<div class='countdown_leg'>${mString}</div>
			</div>`;
		}
		if (aFormat.includes("S")){
			str += `
			<div class='countdown_section'>
			<div class='countdown_num'>${sNum}</div>
			<div class='countdown_leg'>${sString}</div>
			</div>`;
		}


		jqel.html(`<div class='countdown_row'>${str}</div>`);

        // If pause;
        //-----------------------------------
		if (play==false) clearInterval(x);

        // DEV
        //-----------------------------------
		testIndex ++;

        // If the count down is finished, write some text
		if (distance < 0 
            //|| testIndex == 5 
			) {

			clearInterval(x);
		jqel.html("");

                // The callback call
                //-----------------------------------
		if (callback) {
			callback();
		}

	}
}, 1000);
}

/**
 * [b64Encrypt description]
 * @param  {[type]} str [description]
 * @return STRING     [description]
 */
function b64Encrypt(str){
	return btoa(unescape(encodeURIComponent(str)));
}

/**
 * [b64Decrypt description]
 * @param  {[type]} b64 [description]
 * @return STRING     [description]
 */
function b64Decrypt(b64){
	return decodeURIComponent(escape(window.atob(b64)));
}

/**
 * [addMinutes description]
 * @param {[type]} date    [description]
 * @param {[type]} minutes [description]
 * USAGE : 
 *     let date_insert = '2024-03-18 14:47:11'
 *     const date = new Date(Date.parse(date_insert.replace(/-/g, '/')));
 */ 
function addMinutes(date, minutes) {
	date.setMinutes(date.getMinutes() + minutes);
	return date;
}


/**
 * Suffle an array form a seed (a Number).
 * Means there is one shuffle result from a seed.
 * See : https://stackoverflow.com/questions/16801687/javascript-random-ordering-with-seed
 * @param  ARRAY array [description]
 * @param  NUMBER seed [description]
 * @return ARRAY       [description]
 */
function shuffle_seed(array, seed) {
	var m = array.length, t, i;

       // While there remain elements to shuffle…
	while (m) {
         // Pick a remaining element…
		i = Math.floor(random_seed(seed) * m--);
         // And swap it with the current element.
		t = array[m];
		array[m] = array[i];
		array[i] = t;
		++seed
	}

	return array;
}

/**
 * Suffle an array form a seed (a Number)
 * @param  NUMBER seed [description]
 * @return {[type]}      [description]
 */
function random_seed(seed) {
	var x = Math.sin(seed++) * 10000; 
	return x - Math.floor(x);
}


/**
* The vpProgress class.
* @return {[type]} [description]
*
* // Call
//-----------------------------------
// <div id='progress1' class='vpProgress'></div>
// let vpProg = new VpProgress()
// vpProg.init('progress1', 10)

// $("#XXXX").append(`<div id='progressXXXX' class='vpProgress'></div>`)
// vpProg = new VpProgress()
// vpProg.init('progressPressStartButtonAllowed', pressStartButtonAllowedDelay, 
      {removeAtEnd:true, // note:self.lang.LANG_btl_press_start_allowed_delay, callback:function(){ }
// })

*/
let VpProgress = function(){
	let self = this;
	let el, duration, timeout, w, removeAtEnd, callback, elId
	let timeElapsed = 0
	let hasBeenKilled = false
    let step = 60
   let rand = random(999)
   // Against Date.prototype.getTime hack
   //-----------------------------------
   let vpDate = Date;
   
   /**
    * { function_description }
    *
    * @param      {string}  elId       The dom element id
    * @param      {int}  _duration  The duration in seconds
    * @param      {object}  options    The options object
    *                                  {  
    *                                     removeAtEnd:true, 
    *                                     note: "",  
    *                                     callback:function(){}
    *                                  }
    */   
   this.init = function(_elId, _duration, options){
      // Options :
      //-----------------------------------
   	if(typeof options === "undefined"){ options = {};}

   	removeAtEnd = typeof options.removeAtEnd !== 'undefined' ? options.removeAtEnd : true
   	let note = typeof options.note !== 'undefined' ? options.note : ""
   	callback = typeof options.callback !== 'undefined' ? options.callback : function(){}

      // Set
      //-----------------------------------
   	duration = _duration
   	elId = _elId
   	el = $("#"+elId)
   	el.append(`<div class='vpProgressContainer'><div class='vpProgressBar'></div></div>`)
      // Note
      //-----------------------------------
   	if (note !== ''){
   		$(".vpProgressContainer", "#"+elId).append(`<div class='vpProgressNote'>${note}</div>`)
   	}
      // Action
      //-----------------------------------
   	this.launch()
   }
   /**
    * [launch description]
    * @return     {[type]}  [description]
    */
   this.launch = function(){

   	$(".vpProgressBar", el).css('width', '0%')

   	this.doTimer(duration * 1000, 10, 
   		function (steps){
   			timeElapsed += steps
   			w = (timeElapsed / (duration * 1000)) * (100 / (duration * 10))
   			if (w <= 1){
   				$(".vpProgressBar", el).css('width', (w*100)+'%')
   			}else{
   				$(".vpProgressBar", el).css('width', '100%')
   			}
   		},
   		function (){
   			window.clearTimeout(timeout);
   			if(removeAtEnd) el.remove()
   				if(!hasBeenKilled) callback();
   		}
   		)
   }

   //
   // Code from http://www.sitepoint.com/creating-accurate-timers-in-javascript/
   //
   // @param      {number}    length      The length
   // @param      {number}    resolution  The resolution
   // @param      {Function}  oninstance  The oninstance
   // @param      {Function}  oncomplete  The oncomplete
   //
   this.doTimer = function(length, resolution, oninstance, oncomplete){
   	let steps = (length / 100) * (resolution / 10)
   	let speed = length / steps
   	let count = 0
   	let start = vpDate.now();
   	function doTimerInstance(){
   		if (count++ == steps){
   			oncomplete(steps, count);
   		}else{
   			oninstance(steps, count);
   			var diff = (vpDate.now() - start) - (count * speed);
   			timeout = window.setTimeout(doTimerInstance, (speed - diff));
   		}
   	}
   	timeout = window.setTimeout(doTimerInstance, speed);
   };

   /**
    * Kill the vpProg
    */
   this.kill = function(){
   	window.clearTimeout(timeout);
   	el.remove()
   	hasBeenKilled = true
   }
}

/**
 * { function_description }
 *  The only argument is an object, o = {}
 * @param      {<dom element>}  o.el        jQuery selector
 * @param      {string}         o.type      "from" or "to"
 * @param      {<string>}       o.css      the css propoerty
 * @param      {<int>}          o.px        Pixels
 * @param      {<type>}         o.duration      The duration (milli secondes)
 * @param      {string}         o.fade     "in" or "out" or ""
 * @param      {number}         o.delay     The delay (milli secondes)
 * @param      {string}         o.ease      The ease 
 *                                                  easeOutCubic
 *                                                  easeOutBack
 *                                                  easeOutBounce
 *                                                  easeOutElastic
 *                                                  ( https://easings.net/fr )
 * @param      {Function}       o.callback  a function
 */
function vpTransition(o){
    if (typeof o.fade == 'undefined') o.fade = ""
    if (typeof o.delay == 'undefined') o.delay = 0
    if (typeof o.ease == 'undefined' || o.ease == "") o.ease = "easeOutBack"
    if (typeof o.callback == 'undefined') o.callback = function(){}

    let el       = o.el      
    let type     = o.type    
    let css      = o.css     
    let px       = o.px      
    let duration = o.duration
    let fade     = o.fade    
    let delay    = o.delay   
    let ease     = o.ease    
    let callback = o.callback

    let destPos, currentPos, destOpacity, currentOpacity

    // Fade
    //-----------------------------------
    currentOpacity = $(el).css("opacity")
    if (fade == 'in'){
        $(el).css('opacity', 0)
        $(el).show()
        destOpacity = currentOpacity
    }else if (fade == 'out'){
        $(el).css('opacity', currentOpacity)
        destOpacity = 0
    }else{
        destOpacity = currentOpacity
    }

    // Move
    //-----------------------------------
    try{
        currentPos = parseInt($(el).css(css).replace(/[^-\d\.]/g, ''));

    }catch(e){
        currentPos = 0
    }

    if (type == "from"){
        $(el).css(css, currentPos - px);
        destPos = currentPos;
    };
    if (type == "to"){
        destPos = px += currentPos;
    };
    // Anim
    //-----------------------------------
    $(el).delay(delay).animate({[css]:destPos, opacity:destOpacity}, duration, ease, callback);
        
}
// Easy call to vpTransition : 
function vpTrans(el, type, css, px, fade, delay, ease, callback){
    vpTransition({el:el, type:type, css:css, 
                        px:px, duration:duration, delay:delay, fade:fade, 
                        ease:ease, callback:callback})
}

/**
 * Apply a 
 *
 * @param      {object}  o       { parameter_description }
 *  let o.el     a string of the jqieru selector
    let from     string : the value + the unit the font-size will have at start
    let to       String : the value + the unit the font-size will have at end
    let delay    Int (micro s)
    let ease     String ( https://easings.net/fr )
    let callback   function
    let fade     'in' OR 'out' OR ''
 * 
 * EX : vpPlopFontSize({el:'#buttonStartInBattle', fade:'in', duration: 1000, from:'30px', to:'80px', delay:0, ease:'easeInOutBack'})
 */
function vpPlopFontSize(o){

    if (typeof o.fade == 'undefined') o.fade = ""
    if (typeof o.delay == 'undefined') o.delay = 0
    if (typeof o.ease == 'undefined' || o.ease == "") o.ease = "easeOutElastic"
    if (typeof o.callback == 'undefined') o.callback = function(){}
    if (typeof o.from == 'undefined' || o.from == '') o.from = $(o.el).css("font-size")

    let el = o.el
    let duration = o.duration
    let from = o.from
    let to = o.to
    let delay = o.delay
    let ease = o.ease
    let callback = o.callback
    let fade = o.fade

    // Fade
    //-----------------------------------
    let currentOpacity = $(el).css("opacity")
    let destOpacity = currentOpacity
    if (fade == 'in'){
        $(el).css('opacity', 0)
        $(el).show()
        destOpacity = currentOpacity
    }else if (fade == 'out'){
        $(el).css('opacity', currentOpacity)
        destOpacity = 0
    }

    $(el).css('transition', 'none')

    // Anim
    //-----------------------------------
    $(el)
    .delay(delay)
    .css("font-size", from)
    .animate({fontSize: to, opacity: destOpacity}, duration, ease, callback)

}



/**
 * Simplify the use of event listener.
 *
 * @param      {string}  name    The name
 * @param      {function}  foo     The function that will be call on the listener realisation.
 * // Usage : 
 * 
 * 
 *  // Event Listener creation :
    vpListen('testlistener', function(){
        console.log("Dispatch done", event.detail.test);
    })
    // Dispatch event :
    vpDispatch('testlistener', {test:9999})
 */
function vpEventListen(name, foo, removeAtEnd){
    if (typeof removeAtEnd === 'undefined') removeAtEnd = true
    // If the element doesn't exit
    //-----------------------------------
    if ($("#vpListener").length == 0) $("body").append(`<div id='vpListener' style='display:none;'></div>`)
    // Listener creation
    //-----------------------------------
    document.querySelector("#vpListener").addEventListener(name, function handler(event){
        // The function call. (In this foo(), we can retrieve the value of the event.detail;)
        //-----------------------------------
        foo()
        // We remove
        //-----------------------------------
        if (removeAtEnd){
            document.querySelector("#vpListener").removeEventListener(name, handler)
        }
    });
}

/**
 * { function_description }
 *
 * @param      {string}  name    The name
 * @param      {object}  arg     The object that could be retrieved in event.detail
 */
function vpEventDispatch(name, arg){
    // Default
    //-----------------------------------
    if (typeof arg == 'undefined') arg = {}
    // The dispatch
    //-----------------------------------
    document.querySelector("#vpListener").dispatchEvent(
        new CustomEvent(name,{detail:arg})
    )
}


/**
 * Copy the content of a dom element to the clipboard.
 * The inntext of the element is put in a created transparent
 * input text appended to the body and removed at end process.

 * USAGE : 
 <span id='strCopyXXX'>JEUXGEO - Image mystère</span> 
 <i class='fa fa-copy ico_btn' onclick="javascript:copyText('strCopyXXX')"></i>


 { function_description }

 @param      {STRING}  element_id  [description]
 @param      {string}  options     'toast' : if swalToast exists, will fire a swaltoast.
*/
function copyText(element_id, options) {
    // Options :
    //-----------------------------------
    if(typeof options === "undefined"){ options = "";}
    var a = options.split(" ");
    let needToast = false;
    if (a.indexOf('toast') !== -1){
        if (typeof swalToast !== 'undefined'){
            needToast = true;
        } 
    }
    // The value
    //-----------------------------------
    const str = document.querySelector("#"+element_id).innerText;
    // Create the textinput
    //-----------------------------------
    let itForCopy = document.createElement('input');
    itForCopy.innerHTML = `<input type='text' style='position: absolute; bottom:0px; opacity:0' />`;
    const body = document.body;
    body.append(itForCopy);
    itForCopy.value = str;
    // Select the text field
    itForCopy.select();
    itForCopy.setSelectionRange(0, 99999); // For mobile devices
    // Copy the text inside the text field
    navigator.clipboard.writeText(itForCopy.value);
    // Clean up
    itForCopy.remove();
    // Toast
    //-----------------------------------
    if (needToast){
        swalToast.fire({title:"", html:"Copied !",type:'success'});
    }
}


/**
 * Removes duplicates in an array according to a property name.
 * Keeps only the first found entry of the property;
 * 
 * dedoublonage.
 * 
 * let h = [
    {gameId:1, score:888},
    {gameId:1, score:111},
    {gameId:2, score:222},
    {gameId:3, score:333},
    {gameId:3, score:444},
    ]
    => removeDuplicates(h, 'gameId') :
    [{gameId:1, score:888},
    {gameId:2, score:222},
    {gameId:3, score:333}]
 *
 * @param      {<type>}  array     The array
 * @param      {<type>}  property  The property
 * @return     {<type>}  { description_of_the_return_value }
 */
function arrayRemoveDuplicatesFromProperty(array, property) {
  return Array.from(
    array.reduce((map, obj) => {
      const key = obj[property];
      if (!map.has(key)) {
        map.set(key, obj);
      }
      return map;
    }, new Map()).values()
  );
}

//----------------------------------------------------------
// >> VANILLA JS helpers
//----------------------------------------------------------
function hide(element) {
    try{
        element.style.display = 'none';
    }catch(e){ }
}
// Manages the 'flex' too !
//-----------------------------------
function show(element) {
    try{
        element.style.display = '';
    }catch(e){ }
}

function toggle(element) {
    try{
        if (window.getComputedStyle(element).display === 'none') {
            element.style.display = '';
        } else {
            element.style.display = 'none';
        }
    }catch(e){ }
}



