/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * poolfunctions.js
 *
 * Common functions
 *
 * PHP version 5
 *
 * LICENSE: This source file is subject to version 1.0 of the OpenFlyers
 * license that is available through the world-wide-web at the following
 * URI: http://www.openflyers.com/license/semifreelicense1_0.txt. If you did not receive a
 * copy of the OpenFlyers License and are unable to obtain it through the
 * web, please send a note to contact@openflyers.com so we can mail you
 * a copy immediately.
 *
 * @category    Javascript
 * @package     OpenFlyers
 * @author      Christophe LARATTE <christophe.laratte@openflyers.com>
 * @copyright   2008 OPENFLYERS S.A.R.L. <contact@openflyers.com>
 * @license     http://www.openflyers.com/license/semifreelicense1_0.txt  OpenFlyers License
 * @link       http://www.openflyers.com
 * @since      Wed Nov 19 2008
 */

/**
 * checkAll
 * Check/uncheck all checkboxes
 *
 * @param string container Form id
 * @param string field Field name
 * @param boolean state Uncheck/check
 * @return void
*/
function checkAll(container, field, state) {
    var checkboxes = document.getElementById(container).getElementsByTagName('input');
    var reg = new RegExp('^('+field+')(^\s)*');
    var size = checkboxes.length;

    for (var i=0; i<size; i++) {
        if ( checkboxes[i].type == 'checkbox' && reg.test(checkboxes[i].name) ) {
            checkboxes[i].checked = state;
        }
    }
    return true;
}

/**
 * checkUncheckAllAction
 *
 * Check or uncheck a group of checkboxes with specific behavior. Use it only if there is an "all" checkbox.
 * Behavior :
 * - When "all" is checked then all others checkboxes will be checked too
 * - When all checkboxes are checked except "all" then "all" will be checked
 * - When any checkboxes are checked then "all" will be unchecked
 * - When "all" is unchecked then all others checkboxes will be unchecked too
 *
 * @var HTMLElement element
 * @return void
 */
function checkUncheckAllAction(element) {
    var current         = $(element);
    var currentValue    = current.val();
    var currentSiblings = current.parent().find('input[type="checkbox"]');
    var checkboxAll     = currentSiblings.eq(0);

    if ( current.is(':checked') ) {
        if ( $.isNumeric(currentValue) ) {
            // Check "all" is all options are checked
            if ( current.parent().find('input[type="checkbox"]:gt(0)').not(':checked').length == 0 ) {
                checkboxAll.attr('checked', 'checked');
            }
        }
        else {
            // Check all options is "all" is checked
            currentSiblings.attr('checked', 'checked');
        }
    }
    else {
        if ( $.isNumeric(currentValue) ) {
            // Uncheck "all" is any option is unchecked
            checkboxAll.removeAttr('checked');
        }
        else {
            // Uncheck all options is "all" is unchecked
            currentSiblings.removeAttr('checked');
        }
    }
};

/**
 * checkAllNeighbors
 * 
 * Check/uncheck all checkboxes in the same row
 * 
 * @param {HTMLElement} element The checkbox that was clicked
 * @param {boolean} state The state to set the checkboxes to
 * @return {void}
 */
function checkAllNeighbors(element, state) {
    var checkboxes = $(element).parent().find('input[type="checkbox"]');
    checkboxes.prop('checked', state);
}

/**
 * clone
 * Clone an object (Use this function when you want a "copy by value" of an object and not a "copy by reference"
 *
 * @param  object obj Any object
 * @return object
 */
function clone(obj) {
    if (obj == null || typeof(obj) != 'object') {
        return obj;
    }

    var temp = new obj.constructor(); // changed (twice)

    for( var key in obj ) {
        temp[key] = clone( obj[key] );
    }

    return temp;
}

/**
 * createElement
 * Create a DOM element
 *
 * @param String tag
 * @param Object attributes
 * @param Mixed  content
 * @param jQuery Object parentElement
 * @return jQuery Object
 */
function createElement(tag, attributes, content, parentElement) {
    var element = $( document.createElement(tag) );
    
    if (attributes) {
        element.attr(attributes);
    }
    
    if (content) {
        element.append(content);
    }
    
    if (parentElement) {
        element.appendTo(parentElement);
    }
    
    return element;
}

/**
 * disableEnterKey
 * Return true if enter key is pressed and user is not in a textarea else false
 *
 * @param event e
 * @return boolean
 */
function disableEnterKey(e) {
    var key = (window.event) ? window.event.keyCode : e.which;
    // Return false when Enter is entered on a textarea
    if (e.target.nodeName.toLowerCase() === 'textarea') {
        return false;
    }
    return (key == 13);
}

/**
 * evalFormula
 * eval a formula like Formula::calc PHP function
 *
 * @param  string formula
 * @param  string formulaVariable
 * @param  array[integer]mixed formulaVariableValue Value for each formula variable
 * @return float
 */
function evalFormula(formula, formulaVariable, formulaVariableValue) {
    for (var i in formulaVariable) {
        var pattern = new RegExp(formulaVariable[i], 'g');
        formula = formula.replace( pattern, formulaVariableValue[parseInt(i)] );
    }

    return eval(formula);
}

/**
 * Format a number to digit representation
 *
 * @param integer value
 * @return integer
 */
function format2Digits(value)
{
    return (value < 10) ? '0' + value : value.toString();
}

/**
 * isIE
 * 
 * This function returns true for any version of Internet Explorer
 * 
 * @return boolean
 */
function isIE() {
    return (navigator.userAgent.indexOf('MSIE ') > -1 || navigator.userAgent.indexOf('Trident/') > -1);
}

/**
 * isEdge
 * 
 * This function returns true for any version of Edge
 * 
 * @return boolean
 */
function isEdge() {
    return (navigator.userAgent.indexOf("Edg") > -1);
}

/**
 * jump
 * Move to specified anchor
 *
 * @param formId string form
 * @return void
 */
function jump(formId) {
    if ( document.forms[formId].menuAnchor.value != '' ) {
        document.forms[formId].action = 'index.php?menuAction='+document.forms[formId].menuAnchor.value;
    }
}

/**
* onSuccessDraggableFunction
* Called when the ajax action associated to the draggable Row succeeded
*
* @param response Json returned by the ajax action
* @return void
*/
function onSuccessDraggableFunction(response) {
    // Explore each row that can be dragged and modify the numbering order based on the response received.
    for (var i = 0; i < $('.draggableRow').length; i++) {
        orderNum = response.items[$('.draggableRow')[i].attributes['data-id'].value];
        // When the order number is shown as the title in the dot-grid cell upon hovering it
        if ($('.draggableRow')[i].children[0].classList.contains('moveCursor'))
             $('.draggableRow')[i].children[0].attributes.title.value = orderNum;
        // When the order number is depicted as the content of the Order cell
        else $('.draggableRow')[i].children[1].innerHTML              = orderNum;
    }
}

/**
 * prepareReturn
 * Fill "form" field(s)
 *
 * @param string menuAction
 * @param string menuParameter Key id / An identifier (it depends of the usage)
 * @param string menuParameter2 Field name / Second key id (it depends of the usage)
 * @param string menuParameter3 Field value / Field name (it depends of the usage)
 * @param string menuParameter4 Miscellaneous value
 * @param string sortField
 * @param string sortValue
 * @param string sortOrder
 */
function prepareReturn(menuAction, menuParameter, menuParameter2, menuParameter3, menuParameter4, sortField, sortValue, sortOrder) {
    var currentForm = document.getElementById('form');

    if (typeof currentForm.menuAction !== 'undefined') {
        currentForm.menuAction.value = menuAction;
    }
    else {
    	menuActionElement = createElement('input', {'type': 'hidden', 'name': 'menuAction', 'value': menuAction}, null, currentForm);
    }

    if (typeof currentForm.menuParameter !== 'undefined') {
        currentForm.menuParameter.value = menuParameter;
    }
    else {
    	menuParameterElement = createElement('input', {'type': 'hidden', 'name': 'menuParameter', 'value': menuParameter}, null, currentForm);
    }

    if (typeof currentForm.menuParameterBis !== 'undefined') {
        currentForm.menuParameterBis.value = menuParameter2;
    }
    else {
    	menuParameter2Element = createElement('input', {'type': 'hidden', 'name': 'menuParameter2', 'value': menuParameter2}, null, currentForm);
    }

    if (typeof currentForm.menuParameter3 !== 'undefined') {
        currentForm.menuParameter3.value = menuParameter3;
    }
    else {
    	menuParameter3Element = createElement('input', {'type': 'hidden', 'name': 'menuParameter3', 'value': menuParameter3}, null, currentForm);
    }

    if (typeof currentForm.menuParameter4 !== 'undefined') {
        currentForm.menuParameter4.value = menuParameter4;
    }
    else {
    	menuParameter4Element = createElement('input', {'type': 'hidden', 'name': 'menuParameter4', 'value': menuParameter4}, null, currentForm);
    }

    if (typeof currentForm.sortField !== 'undefined') {
        currentForm.sortField.value = sortField;
    }
    else {
    	sortFieldElement = createElement('input', {'type': 'hidden', 'name': 'sortField', 'value': sortField}, null, currentForm);
    }

    if (typeof currentForm.sortValue !== 'undefined') {
        currentForm.sortValue.value = sortValue;
    }
    else {
        sortValueElement = createElement('input', {'type': 'hidden', 'name': 'sortValue', 'value': sortValue}, null, currentForm);
    }

    if (typeof currentForm.sortOrder !== 'undefined') {
        currentForm.sortOrder.value = sortOrder;
    }
    else {
    	sortOrderElement = createElement('input', {'type': 'hidden', 'name': 'sortOrder', 'value': sortOrder}, null, currentForm);
    }
}

/**
 * redGlowOnRequiredInput
 *
 * Add a red glowing effect on a field which is required when submit
 *
 * @link          https://work.openflyers.com/OF-4-Activity-Form-Layout#Méthode-redGlowOnRequiredInput
 * @param DOMNode HTML element (or his parent) that will glow
 * @param boolean false if form-field is parent, true if elementRequired is a brother from form-field
 * @return        void
 */
function redGlowOnRequiredInput(elementRequired, isBrother) {
    var isBrother = isBrother != undefined ? isBrother : false;
    if (elementRequired) {
        if (elementRequired.className.indexOf('form-field') >= 0) {
            elementRequired.className += ' required';
            // Remove the class updating if it exist
            elementRequired.classList.remove("required");
            /**
             * A line to trigger reflow just to retriggered the animation
             * @link https://css-tricks.com/restart-css-animation/
             */
            void elementRequired.offsetWidth;
            // Add updating class
            elementRequired.classList.add("required");
        }
        else {
            // Call this function recursively on element parent if element not contains form-field class
            redGlowOnRequiredInput(((isBrother && (elementRequired.previousSibling != null)) ? elementRequired.previousSibling : elementRequired.parentElement), isBrother);
        }
    }
}

/**
 * roundCeil
 * used in the formula (see Formula class)
 * Warning: this is a JavaScript equivalent of roundDuration defined in pool/functions.php
 *
 * @access public
 * @param  float value
 * @param  float round
 * @return float
 */
function roundCeil( value, round )
{
    return Math.ceil( value / round ) * round;
}

/**
 * Save the content of a form field into a global variable
 *
 * @param string fieldValue
 * @return void
 */
function saveIntoVariable(fieldValue) {
    fieldMemory = fieldValue;
}

/**
 * setDefaultFileNameForFileInput
 * Define a default file name for the input type 'file'
 *
 * @param DOM fileInput
 * @param string defaultFileName
 * @return void
 */
function setDefaultFileNameForFileInput(fileInput, defaultFileName) {
    const file         = new File([], defaultFileName);
    const dataTransfer = new DataTransfer();
    dataTransfer.items.add(file);
    fileInput.files = dataTransfer.files;
}

/**
 * setDisabledButtonsVisibility
 * 
 * @param {boolean} visible
 * @param {array} disabledButtons
 * @return {void}
 */
 function setDisabledButtonsVisibility(visible, disabledButtons) {
        for(const disabledButton of disabledButtons) {
            // If the toggle switch is on state 'on'
            if (visible) {
                disabledButton.attr("disabled", false);
            }
            // If the toggle switch is on state 'off'
            else {
                disabledButton.attr("disabled", true);
            }
        }
}

/**
 * sortObjectToArrayOfArrays
 * 
 * Convert an object to an array of arrays and sort it
 * 
 * @param Object obj
 * @return Array
 */
function sortObjectToArrayOfArrays(obj) {
    // Convert object to array of arrays
    var arrayOfArrays = Object.keys(obj).map(key => [key, obj[key]]);

    // Sort the array
    arrayOfArrays.sort((firstArray, secondArray) => {
        if (firstArray[0] === '' && secondArray[0] !== '') {
            // Always place '' (empty string) at the beginning
            return -1;
        } else if (firstArray[0] !== '' && secondArray[0] === '') {
            // Place '' at the beginning if secondArray is the empty string
            return 1;
        } else {
            // Sort remaining elements alphabetically
            return firstArray[0].localeCompare(secondArray[0]);
        }
    });

    return arrayOfArrays;
}

/**
 * submitDate
 * Set menuParameter4 field with a built-in date
 *
 * @param integer year
 * @param integer month
 * @param integer day
 * @return void
 */
function submitDate(year, month, day) {
    var current                     = document.getElementById('form');
    month                           = format2Digits(month);
    day                             = format2Digits(day);
    current.menuParameter4.value    = year.toString()+'-'+month+'-'+day;
    current.submit();
}

/**
 * submitReturn
 * Fill "form" field(s) and submit it
 *
 * @param string action
 * @param string parameter
 * @param string parameter2
 * @param string parameter3
 * @param string parameter4
 * @param string sortField
 * @param string sortValue
 * @param string sortOrder
 * @param string allowSubmitFunction Name of the function to allow form submit
 * @return void
 */
function submitReturn(action, parameter, parameter2, parameter3, parameter4, sortField, sortValue, sortOrder, originalValue, allowSubmitFunction) {
//    console.dir(arguments);
    // The value has not changed
    if (originalValue != undefined && parameter3 === originalValue) {
        return;
    }
    prepareReturn(action, parameter, parameter2, parameter3, parameter4, sortField, sortValue, sortOrder);

    var currentForm = document.getElementById('form');
    if ( currentForm.menuParameter3.value != fieldMemory ) {
        fieldMemory = null;
        if ( window[allowSubmitFunction] != undefined ) {
            if ( window[allowSubmitFunction].call(this, parameter) ) {
                currentForm.submit();
            }
        }
        else {
            currentForm.submit();
        }
    }
}

/**
 * toggleSwitch
 *
 * Enables or disables the toggle switch passed as a parameter
 *
 * @param {*} e the toggle switch
 * @param {*} hiddenName the hidden input name
 */
function toggleSwitch(e, hiddenName = '') {
    var toggleSwitch = $(e.parentNode);
    if ( toggleSwitch.hasClass('disabled') ) return;

    // If the toggle switch is enable
    if ( !toggleSwitch.hasClass('active') ) {
        toggleSwitch.addClass('active');
        $('#' + hiddenName).val(1);
    } else {
        toggleSwitch.removeClass('active');
        $('#' + hiddenName).val(0);
    }
}

/**
 * translate
 * 
 * @link https://work.openflyers.com/index.php?title=OF4_Application_engine#Fonction_translate
 *
 * @param String tag
 * @param Array  parameters
 * @return String
 */
function translate(tag, parameters) {
    if (translationTag == undefined) {
        return '';
    }
    var text = translationTag[tag];
    if (!text) {
        text = tag;
    }
    else if (parameters != undefined) {
        for ( var i = 0; i < parameters.length; i++) {
            // Pattern list
            var patternList = {
                '%01.2f': text.indexOf('%01.2f'),
                '%s'    : text.indexOf('%s')
            };
            // Search next pattern
            var pattern   = null,
                textIndex = null;
            for ( var key in patternList) {
                if (!patternList.hasOwnProperty(key)) { continue; }
                if (patternList[key] != -1 && (!textIndex || patternList[key] < textIndex)) {
                    textIndex = patternList[key];
                    pattern   = key;
                }
            }
            if (!pattern) {
                break;
            }
            // Replaces text
            var replacementText = null;
            switch (pattern) {
                case '%01.2f':
                    var currency    = new Currency();
                    replacementText = currency.format(parameters[i], 4, '');
                    break;
                default:
                    replacementText = parameters[i];
                    break;
            }
            text = text.replace(pattern, replacementText);
        }
    }
    return text;
}

/**
 * trim
 * Delete spaces at the beginning and the end of a string
 *
 * @access public
 * @param  s string
 * @return string
 */
function trim(s) {
    return s.replace(/^\s+/, '').replace(/\s+$/, '');
}

/**
 * viewAccount
 * Set parameter field with account id
 *
 * @param integer account Account id
 * @return void
 */
function viewAccount(account) {
    var current                 = document.getElementById('values') || document.getElementById('form');
    current.menuAction.value    = 'account_journal';
    current.menuParameter.value = account;
    current.submit();
}

/**
 * yellowGlowOnModifiedInput
 *
 * Add a yellow glowing effect on a field which is automatically changed
 *
 * @link          https://work.openflyers.com/OF-4-Activity-Form-Layout#Méthode-yellowGlowOnModifiedInput
 * @param DOMNode HTML element (or his parent) that will glow
 * @return        void
 */
function yellowGlowOnModifiedInput(elementModified){
    if (elementModified) {
        if ( elementModified.className.indexOf('form-field') >= 0 ) {
            // Remove the class updating if it exist
            elementModified.classList.remove("updating");
            /**
             * A line to trigger reflow just to retriggered the animation
             * @link https://css-tricks.com/restart-css-animation/
             */
            void elementModified.offsetWidth;
            // Add updating class
            elementModified.classList.add("updating");
        }
        else {
            // Call this function recursively on element parent if element not contains form-field class
            yellowGlowOnModifiedInput(elementModified.parentElement);
        }
    }
}