Source: Util_lib/process.js

import("system.neon");
import("system.project");
import("system.logging");
import("system.process");
import("system.db");
import("system.util");
import("system.translate");
import("system.text");
import("system.vars");
import("system.swing");
import("system.question");
import("system.eMath");
import("system.datetime");
import("Offer_lib");
import("Date_lib");

/**
 * Class containing utility functions for images
 * 
 * @class
 *
 */
function ImageUtils()
{
    var that = this;
    /**
    * reads a picture depending on description and icon_type from ASYS_ICONS
    * @param {String} pIconType requ ICON_TYPE of ASYS_ICONS
    * @param {String} pDescription requ DESCRIPTION of ASYS_ICONS
    * @param {Boolean} pBase64 opt true/undefined: BINDATA (Tables, trees) or false: ID (Icons, XMPP picture)
    * 
    * @return {String} BINDATA of ASYS_ICONS
    * 
    * 
    */
    this.getIcon = function(pIconType, pDescription, pBase64)
    {
        var field = pBase64 || pBase64 == undefined ? "BINDATA" : "ID";
        return db.cell("select " + field + " from ASYS_ICONS where DESCRIPTION = '" + pDescription + "' and ICON_TYPE = '" + pIconType + "'",
            "_____SYSTEMALIAS");
    }

    /**
    * reads frequently used combination of description and bindata/id from ASYS_ICONS
    * @param {String} pIconType ICON_TYPE of ASYS_ICONS
    * @param {Boolean} pBase64 opt true: BINDATA or false/undefined: ID
    *
    * @return {String[][]} Key: Description, Value: ID
    * 
    */
    this.getImageObject = function (pIconType, pBase64)
    {
        var field = pBase64 ? "BINDATA" : "ID";

        var icons = {};//kein Array sondern Object, da es sich um ein assoziatives Array handelt
        var data = db.table("select " + field + ", DESCRIPTION from ASYS_ICONS where ICON_TYPE = '" + pIconType + "'",
            "_____SYSTEMALIAS");
        for (var i = 0; i < data.length; i++ )
            icons[data[i][1]] = data[i][0];

        return icons;
    }
}
/**
 * class containing utils for URLs
 * @class
 */
function UrlUtils()
{
    var that = this;
    /** 
    * Extracts a query param from an URL
    *
    * @param {String} pURL the text from which the parameter should be read
    * @param {String} pParameter Name of the parameter
    *  (Messaging mit Default Trenner:
    *  - im.internal: "id=", "title_ack=", "title_dcl=", "text_ack=", "text_dcl=", "desc=", "name=", "result=", "decidable=", "icon=", "icon_ack=", "icon_dcl="
    *  - im.file: "id=", "size=", "result=", "requestor=")
    * @param {String} pSplit opt der String, which marks the end of the param value, standard is '&'
    *
    * @return {String} the extracted value or null if param not existent
    * 
    * 
    */
    this.extractBlock = function(pURL, pParameter, pSplit)
    {
        var idLeft = pURL.indexOf(pParameter, 0);
        if(idLeft <= -1)
            return null; // Kein Block vorhanden
        if(pSplit != undefined) var idRight = pURL.indexOf(pSplit, idLeft);
        if(pSplit == undefined || idRight <= -1) idRight = pURL.indexOf("&", idLeft);
        if(idRight <= -1)
            idRight = pURL.length;

        return pURL.substring(idLeft + pParameter.length, idRight);
    }

    /**
    * opens the browser
    *
    * @param {String} pUrl req
    * @param {String} pBrowserType opt Browser type 1 = Native (Internet Explorer Rendering Engine)
    *                              ,2 = JavaFX (WebKit Rendering Engine), 3 = Native, if possible.
    *                              default is 2;
    *
    * @return {void}
    * 
    *
    */
    this.openUrl = function(pUrl, pBrowserType)
    {
        if ( pUrl != "" )
        {
            if (pUrl.substr(0, 4) != "http"
                && pUrl.substr(0, 4) != "ftp:"
                && pUrl.substr(0, 5) != "file:")
                {
                pUrl = "http://" + pUrl;
            }

            var browserType = pBrowserType || 2;
            swing.openBrowser(true, false, encodeURI(pUrl), swing.WINDOW_CURRENT, [], browserType);
        }
    }
}
/**
 * Class containing String utility functions
 * @class
 * 
 */
function StringUtils()
{
    var that = this;
    /**
    * validates if a string is a UUID according to versions 1 through 5 of RFC4122
    * @param {String} pUUID String to be validated
    * @param {bool} pIgnoreCase true if case sensitivty isn't needed
    *
    * @return {Boolean} ob es sich um eine UUID handelt
    * 
    * 
    */
    this.isValidUUID = function(pUUID, pIgnoreCase)
    {
        if (pIgnoreCase)
        {
            pUUID = pUUID.toLowerCase();
        }

        return new RegExp(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/).test(pUUID);
    }
    /**
    * Returns wether a string starts with given string or not.
    * @param pString String, which is being searched in
    * @param pStringToSearch String, which is being searched
    * 
    * @return {Boolean}
    * 
    * 
    */
    this.stringStatrsWith = function(pString, pStringToSearch)
    {
        return pString.indexOf(pStringToSearch) === 0;
    }
    /**
    * Splits street and buildingnumber
    *
    * @param {String} pAddress req Address
    *
    * @return {Array}  [ Street, BuildingNr ]
    * 
    * 
    */
    this.splitAddress = function(pAddress)
    {
        var ret = ["",""];
        if(pAddress != "")
        {
            var arr = pAddress.match( /^[^0-9]+|[0-9]+.*$/g);
            ret[0] = arr[0].trim();
            if(arr[1]) 
                ret[1] = arr[1].trim();
        }
        return ret;
    }

    /**
    * Generates the string which shows, who created the dataset and who edited it
    *
    * @param {String} pDateNew req Date of creation
    * @param {String} pUserNew req name of the user, who created the dataset 
    * @param {String} pDateEdit req date of the last change
    * @param {String} pUserEdit req name of last editor
    *
    * @return {String}
    * 
    * 
    */
    this.label_new_edit = function(pDateNew, pUserNew, pDateEdit, pUserEdit)
    {
        var retst = translate.withArguments("created on %0 by %1", [ datetime.toDate(pDateNew, translate.text("yyyy-MM-dd")), pUserNew]);
        if (pDateEdit != undefined && pDateEdit != "")  retst += " | " + translate.withArguments("changed on %0 by %1", [ datetime.toDate(pDateEdit, translate.text("yyyy-MM-dd")), pUserEdit]);
        return(retst)
    }
    
    /**
    * uses the right translate method, depending on the parameters
    *
    * @param {String} pText string to be translated
    * @param {String} pLocale locale for translating
    *
    * @return {String}
    *
    * 
    */
    this.translateStr = function( pText, pLocale )
    {
        if ( pLocale == undefined )   
            return translate.text(pText);
        else 
            return translate.text(pText, pLocale)
    }
    

}
/**
 * Class containing utility functions for use with arrays
 * @class
 */
function DataUtils()
{
    var that = this;
    /**
    * sorts a two dim array by the given index
    *
    * @param {Array} pArray req the Array to be sorted
    * @param {String} pIndex req the index of the field to sort by
    * @param {Boolean} pUp req TRUE sorts ascending, FALSE sorts decending
    * @param {Boolean} isNumber TRUE sorts numerical, FALSE or undefined sorts alphanumerical
    *
    * @return void
    *
    * @throws {Error} if index is outside pArray.length
    * 
    * 
    */
    this.array_mDimSort = function(pArray, pIndex, pUp, isNumber)
    {
        if (pArray.length == 0)
            return;

        if (pArray[0].length == 0)
            return;

        if (pIndex >= pArray[0].length)
            throw new Error("Index Out Of Bounds: " + pIndex + " >= " + pArray[0].length);

        /*
        * the sort function
        *
        * @param {String} x req value 1
        * @param {String} y req value 2
        *
        * @return {Integer}
        * 
        * @memberof array_mDimSort
        */
        function array_mDimSorter(x, y)
        {
            if ( isNumber )
            {
                xx = Number(x[pIndex]);
                yy = Number(y[pIndex]);
            }
            else
            {
                xx = x[pIndex];
                yy = y[pIndex];

                //Umlaute austauschen
                xx = xx.toLowerCase();
                xx = xx.replace(/ä/g,"ae");
                xx = xx.replace(/ö/g,"oe");
                xx = xx.replace(/ü/g,"ue");
                xx = xx.replace(/ß/g,"ss");

                yy = yy.toLowerCase();
                yy = yy.replace(/ä/g,"ae");
                yy = yy.replace(/ö/g,"oe");
                yy = yy.replace(/ü/g,"ue");
                yy = yy.replace(/ß/g,"ss");
            }
            if (xx == yy)
                return 0;

            if (xx < yy)
                return (pUp ? -1 : 1);

            return (pUp ? 1 : -1);
        }

        pArray.sort(array_mDimSorter);
    }

    /**
    * sorts an array up to 6 columns with sortorder
    *
    * @param {Array} pArray req the array with data
    * @param {Integer} us req  the Sortorder for Column 1 = Param u (1=asc, -1=desc)
    * @param {Integer} u req the 1 Column
    * @param {Integer} vs opt  the Sortorder for Column 2 = Param v (1=asc, -1=desc)
    * @param {Integer} v opt the 2 Column
    * @param {Integer} ws opt  the Sortorder for Column 3 = Param w (1=asc, -1=desc)
    * @param {Integer} w opt the 3 Column
    * @param {Integer} xs opt  the Sortorder for Column 4 = Param x (1=asc, -1=desc)
    * @param {Integer} x opt the 4 Column
    * @param {Integer} ys opt  the Sortorder for Column 5 = Param y (1=asc, -1=desc)
    * @param {Integer} y opt the 5 Column
    * @param {Integer} zs opt  the Sortorder for Column 6 = Param z (1=asc, -1=desc)
    * @param {Integer} z opt the 6 Column
    *
    * @return {void}
    * 
    * 
    */
    this.sortArray = function(pArray, us, u, vs, v, ws, w, xs, x, ys, y, zs, z)
    {
        pArray.sort(_sortmulti);
        /*
        * sort of a two dim array, up to 6 columns
        *
        * @param {String} a req value 1, first compared element
        * @param {String} b req value 2, sencond compared element
        *
        * @return {Integer} -1 - set a below b, 0 - equal, 1 - set b below a 
        */
        function _sortmulti(a, b)
        {
            var stringComparison = function(a, b)	
            {
                a = a.toLowerCase();
                a = a.replace(/ä/g,"ae");
                a = a.replace(/ö/g,"oe");
                a = a.replace(/ü/g,"ue");
                a = a.replace(/ß/g,"ss");

                b = b.toLowerCase();
                b = b.replace(/ä/g,"ae");
                b = b.replace(/ö/g,"oe");
                b = b.replace(/ü/g,"ue");
                b = b.replace(/ß/g,"ss");

                return( a == b ) ? 0 : ( a > b ) ? 1 : -1;
            }
            
            
            var swap=0;

            if (isNaN(a[u] - b[u])) // if there is a string in the first compared element
                if( isNaN(a[u]) && isNaN(b[u]) ) // if both are strings,
                    swap = stringComparison(a[u], b[u]); // then: true - false = 1; false - true = -1
                else
                    swap = (isNaN(a[u]) ? 1 : -1);
            else
                swap = (a[u] - b[u]);

            if ((v == undefined) || (swap != 0))
                return swap * us;
            else
            if (isNaN(a[v] - b[v]))
                if ((isNaN(a[v])) && (isNaN(b[v])))
                    swap =  stringComparison(a[v], b[v]);
                else
                    swap = (isNaN(a[v]) ? 1 : -1);
            else
                swap = (a[v] - b[v]);

            if ((w == undefined) || (swap != 0))
                return swap * vs;
            else
            if (isNaN(a[w] - b[w]))
                if ((isNaN(a[w])) && (isNaN(b[w])))
                    swap =  stringComparison(a[w], b[w]);
                else
                    swap = (isNaN(a[w]) ? 1 : -1);
            else
                swap = (a[w] - b[w]);

            if ((x == undefined) || (swap != 0))
                return swap * ws;
            else
            if (isNaN(a[x] - b[x]))
                if ((isNaN(a[x])) && (isNaN(b[x])))
                    swap =  stringComparison(a[x], b[x]);
                else
                    swap = (isNaN(a[x]) ? 1 : -1);
            else
                swap = (a[x] - b[x]);

            if ((y == undefined) || (swap != 0))
                return swap * xs;
            else
            if (isNaN(a[y] - b[y]))
                if ((isNaN(a[y])) && (isNaN(b[y])))
                    swap =  stringComparison(a[y], b[y]);
                else
                    swap = (isNaN(a[y]) ? 1 : -1);
            else
                swap = (a[y] - b[y]);

            if ((z == undefined) || (swap != 0))
                return swap * ys;
            else
            if(isNaN(a[z] - b[z]))
                if((isNaN(a[z])) && (isNaN(b[z])))
                    swap =  stringComparison(a[z], b[z]);
                else
                    swap = (isNaN(a[z]) ? 1 : -1);
            else
                swap = (a[z] - b[z]);

            return swap * zs;
        }

    }

    /**
    * removes an element from an array
    *
    * @param {Array} pArray req Array from which the element should be removed
    * @param {String} pElement req index of the element which should be removed
    *
    * @return {Array} array without the removed element
    * 
    * 
    */
    this.removeElement = function(pArray, pElement)
    {
        return pArray.splice(pElement, 1);
    }
    
    /**
    * returns all elemtens not in pSource
    *
    * @param {Array} pSource req this elements are being searched
    * @param {Array} pReference req in this array the search is done
    * @param {Boolean} pIgnoreCase opt
    *
    * @return {Array[]} resultIn
    * 
    * 
    */
    this.notIn = function(pSource, pReference, pIgnoreCase)
    {
        var resultIn = new Array();
        for (var i = 0; i < pSource.length; i++)
        {
            if (!that.hasElement(pReference, pSource[i], pIgnoreCase))
                resultIn.push(pSource[i]);
        }

        return resultIn;
    }    

    /**
    * returns if an element is in an array.
    *
    * @param {Array} pArray req array which should be searched in 
    * @param {String} pElement req Element which should looked for
    * @param {Boolean} pIgnoreCase opt
    *
    * @return {Boolean} true if it has the element
    * 
    * 
    */
    this.hasElement = function(pArray, pElement, pIgnoreCase)
    {
        for (var i = 0; i < pArray.length; i++)
        {
            if ( pIgnoreCase != undefined && pIgnoreCase )
            {
                if (pArray[i].toLowerCase() == pElement.toLowerCase())	
                    return true;
            }
            else	
                if (pArray[i] == pElement)	
                    return true;
        }

        return false;
    }
    
    /**
     * concats arrays column by column
     * see example for more details
     * logging.show(JSON.stringify(concatArrayColumns(a, b)));
     * //[["a","A"],["b","B"],["c","C"]]
     *
     * logging.show(JSON.stringify(a.concat(b)));
     * //["a","b","c","A","B","C"]
     * @param {Array} pArray1 req you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want
     * @param {Array} pArrayN req you have to pass at least 2 Arrays that shall be concated but you can pass as many as you want
     *
     *
     *
     * @return {Array} Beschreibung
     * 
     * 
     */
    this.concatArrayColumns = function(pArray1, pArrayN)
    {
        var res = [];

        for (var i = 0, l = arguments.length; i < l; i++)//this function can handle an "unlimited" amount of functionparams
        {
            var inpArr = arguments[i];
            for (var ii = 0, ll = inpArr.length; ii < ll; ii++)
            {
                if (res[ii] == undefined)
                    res[ii] = [];

                    res[ii] = res[ii].concat(inpArr[ii]);
            }
        }
        return res;
    }
    
    /**
    *  function to determine, if an object is truly an Array
    *  @param {Object} pArray
    *  @return {Boolean} returns wether an Object is an Array (true) or not (false)
    *  
    *  
    */
    this.isArray = function(pArray)
    {
        return (pArray && typeof pArray === "object" && typeof pArray.length === "number" && !(pArray.propertyIsEnumerable('length')))          
    }
}
/**
 * Class containing utility functions for table components
 * @class
 */
function TableUtils()
{
    var that = this;
    /**
    * Moving Tablerows Sort field up or down a step
    *
    * @param {String} pTable req Table
    * @param {String} pTableComp req name of the table comp
    * @param {String} pCountField req sort field name z.B. 'SORT'
    * @param {String} pDirection req 'up' oder 'down'
    * @param {String} pCondition opt
    *
    * @return {void}
    * 
    * 
    */
    this.moveRow = function(pTable, pTableComp, pCountField, pDirection, pCondition)
    {
        that.sortRow(pTable, pCountField, pCondition);
        
        if (pCondition == undefined || pCondition == "") 
            pCondition = "";
        else 
            pCondition = " and " + pCondition;
        
        var id = text.decodeFirst(vars.getString(pTableComp));
        var col = [pCountField];
        var nextval;
        var typ = db.getColumnTypes( pTable, col);
        var oldval = db.cell("select " + pCountField + " from " + pTable + " where " + pTable + "ID = '" + id + "'");
        
        if (pDirection == "up")
        {
            nextval = db.cell("select max(" + pCountField + ") from " + pTable
                + " where " + pCountField + " < " + oldval + pCondition);
        }
        else
        {
            nextval = db.cell("select min(" + pCountField + ") from " + pTable
                + " where " + pCountField + " > " + oldval + pCondition);
        }
        if ( nextval == "" )  
            nextval = 0;
        var nextID = db.cell("select " + pTable + "ID from " + pTable + " where " + pCountField + " = " + nextval + pCondition);
        db.updateData(pTable, col, typ, [oldval], pTable + "ID = '" + nextID + "'")
        db.updateData(pTable, col, typ, [nextval], pTable + "ID = '" + id + "'")
    }

    /**
    * activates Up/Down buttons
    *
    * @param {String} pTable req Table
    * @param {String} pTableComp req name of the table comp
    * @param {String} pSortfield req sort field name z.B. 'SORT'
    * @param {String} pDirection req 'up' or 'down'
    * @param {String} pCondition opt
    *
    * @return {Boolean} true if active
    * 
    * 
    */
    this.moveActive = function(pTable, pTableComp, pSortfield, pDirection, pCondition)
    {
        var oldval = "min";
        var minmax = "min";
        var id = text.decodeFirst(vars.getString(pTableComp));

        if (pCondition == undefined || pCondition == "") 
            pCondition = "";
        else 
            pCondition = " where " + pCondition;
        
        if (id != "" && swing.getTableDataByAttribute(pTableComp, swing.INSERTED ).length == 0 && vars.getString("$sys.workingmode") == swing.FRAMEMODE_EDIT )
        {
            oldval = db.cell("select " + pSortfield + " from " + pTable + " where " + pTable + "ID = '" + id + "'");
            
            if(pDirection == "down")	
                minmax = "max";
            
            minmax = db.cell("select " + minmax + "(" + pSortfield + ") from " + pTable + pCondition);
        }
        return (oldval != minmax || oldval == "" || minmax == "" )
    }
}
/**
 * Class containing utility functions for use with base64 and binaries
 * @class
 */
function BinaryUtils()
{
    var that = this;
    /**
    * decodes 64Based String
    *
    * @param {String} input req the string
    *
    * @return {String} decoded String
    * 
    * 
    */
    this.decode64 = function(input)
    {
        var charset = new Configuration().getOption("Base64Charset");

        if ( charset == "" )
            charset = "ISO-8859-15";
        return util.decodeBase64String(input, charset);
    }
    /**
    * returns the original size of a B64 coded String in bytes
    *
    * @param {String} pBase64str req base64 encoded string
    *
    * @return {Number} size of the string in bytes
    * 
    * 
    */
    this.getBase64lengthBytes = function(pBase64str)
    {
        var res = pBase64str.length;
        res = eMath.divDec(res, 4);
        res = Math.ceil(res);
        res = eMath.mulInt(3, res);

        return +res;//cast und return
    }
    
    /**
     *  sanitizes the filelist gotten from Drag&Drop and returns it.
     *  
     *  @param {String[]} pFilelist req Die Filelist als Stringarray.
     *  
     *  
     */
    this.sanitizeFilelist = function(pFilelist)
    {
        var sanitized = [];
        
        if(pFilelist != undefined && pFilelist.length > 0)
        {
            for(let i = 0; i < pFilelist.length; i++)
            {
                if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_ISDIRECTORY, [pFilelist[i]]) == "false")
                    sanitized.push(pFilelist[i]);
            }
        }
        
        return sanitized;
    }
    /**
     *  reads the folders in a filelist and returns their contained files
     *
     *  @param {String[]} pFilelist req Die Filelist als Stringarray.
     *  
     *  
     */
    this.resolveSubfolders = function(pFilelist)
    {
        //read systemconfig for D&D
        var cfg = new Configuration();
        var filesize   = (cfg.getOption("dnd.maxFileSize") *1000 *1000 );
        var fileamount = 30;
        var files = [];
        var folders = [];

        if(pFilelist != undefined && pFilelist.length > 0)
        {
            //separate files and folders
            for (let i = 0; i < pFilelist.length; i++)
            {
                if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_ISDIRECTORY, [pFilelist[i]]) == "false")
                    files.push(pFilelist[i]);
                else
                    folders.push(pFilelist[i]);
            }

            //resovle Files in Folders
            if(folders.length > 0)
            {
                for (let j = 0; j < folders.length; j++)
                {
                    var resolvedFiles = swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_LISTFILES, [folders[j]]);
                    if(resolvedFiles.length > 0)
                    {
                        for (let k = 0; k < resolvedFiles.length; k++) resolvedFiles[k] = folders[j] + "\\" + resolvedFiles[k];
                        files = files.concat(resolvedFiles);
                    }
                }
            }

            //recursive call, cause more subfolders may be present
            if( that.hasSubfolders(files) )
                files = that.resolveSubfolders(files);
        }

        //filter out files, that exceed the size limit
        var retfiles = [];
        for (let m = 0; m < files.length; m++)
        {
            if(retfiles.length == fileamount)
                break;

            if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_GETLENGTH, [files[m]]) <= filesize)
                retfiles.push(files[m]);
        }

        logging.log(retfiles)
        return retfiles;
    }
    /**
     *  Checks if subfolders are present in a filelist
     *
     *  @param {String[]} pFilelist req the file list as string array
     *  
     *  
     */
    this.hasSubfolder = function(pFilelist)
    {
        var ret = false;
        if(pFilelist != undefined && pFilelist.length > 0)
        {
            for (let i = pFilelist.length; i >= 0; i--)
            {
                if(swing.doClientIntermediate(swing.CLIENTCMD_FILEIO_ISDIRECTORY, [pFilelist[i]]) == "true")
                {
                    ret = true;
                    break;
                }

            }
        }
        return ret;
    }
    
}
/**
 * Class containing miscellaneous utiltiy function extending JDito
 * @class
 */
function JDitoUtils()
{
    var that = this;
    /**
     * A wrapper for process.executeScript
     *
     * @param {String} pIdentifier req The identifier for the script (for better error logging)
     * @param {String} pScript req The script to execute
     * @param {Object} pLocalVariables opt The local variables which can be accessed within the script
     * @param {Array} pImports opt To add additional imports  which will be prepended to the script
     * @param {Boolean} pWrapIntoResult opt To wrap pScript into 'result.string("***")'
     * @param {String} pAlias opt The alias to use within the script
     *
     * @return {Object} The result of the script
     *
     */
    this.evalScript = function(pIdentifier, pScript, pLocalVariables, pImports, pWrapIntoResult, pAlias)
    {
        var defaultImports = ["system.result", "system.vars"];
        var importsToPrepend = [];

        // add pImports to to defaultImports
        if (pImports != undefined) defaultImports = defaultImports.concat(pImports);

        // go trough all defaultImports an check if they already exists
        for (var i = 0; i < defaultImports.length; i++ )
        {
            var cImport = "import(\"" + defaultImports[i] + "\");";

            if (pScript.indexOf(cImport) == -1)
            {
                // If the current default import was not found within the script it will be prepended
                importsToPrepend.push(cImport);
            }
        }

        // join the imports to prepend and place them in front of a new variable
        var script2Execute = importsToPrepend.join("\r\n") + (importsToPrepend.length > 0 ? "\r\n" : "");

        // Check if the script needs to be wrapped into result.string() (e.g. (function(){return "result";})())
        if (pWrapIntoResult)
            script2Execute += "\r\nresult.string(" + pScript + ");\r\n"; // Wrap into 'result.string("***")'
        else
            script2Execute += pScript; // Just add the script after the prepended imports

        return process.executeScript("[evalScript]" + pIdentifier, script2Execute, (pLocalVariables != undefined && pLocalVariables != null ? pLocalVariables : {}), (pAlias != undefined && pAlias != null ? pAlias : null));
    }

    /**
     * Returns a list of aliasNames with a given type
     * ignores errors when an alias is not confgiured (empty)
     *
     * @param {project.DATASOURCE_} pAliasType opt the type of the aliases to load
     *
     * @return {String[]} Array of aliasNames
     */
    this.getAliasListByType = function(pAliasType)
    {
        pAliasType = pAliasType || project.DATASOURCE_DB;

        var allDbAliases = project.getDataModels(project.DATAMODEL_KIND_ALIAS);
        var dbAliases = [];

        for (var i = 0, j = allDbAliases.length; i < j; i++)
        {
            var aliasName = allDbAliases[i][0];
            var alias = null;
            try
            {
                alias = project.getAliasModel(aliasName);
            }
            catch(ex)
            {
                logging.log(translate.withArguments("Der Alias \"%0\" konnte nicht geladen werden. Eventuell wurde er nicht konfiguriert", [aliasName]));
                logging.log(ex);
            }

            if (alias != null && alias[project.ALIAS_DATASOURCETYPE] == pAliasType)
                dbAliases.push(aliasName);
        }

        return dbAliases;
    }

    /**
    * returns a sorted and translated list of countries ;
    * DE, AT and CH are at the beginning of the list, if pData is undefined
    *
    * @param {String} pLanguage opt Sprache
    * @param {String[][]} pData opt if the data should not be gathered automatically
    *                               it can be given to this parameter.
    *                               with this, the function can be used to translate country names
    *
    * @return {String[][]} consisting of ISO2 and Name
    *
    * 
    */
    this.getCountries = function(pLanguage, pData)
    {
        var data, temp;
        if(pData == undefined)
        {
            data = [ ["DE", "Deutschland"], ["AT", "Österreich"], ["CH", "Schweiz"] ];
            temp = db.table("select COUNTRYINFO.ISO2, COUNTRYINFO.NAME_DE "
                + "from COUNTRYINFO "
                + "where COUNTRYINFO.ISO2 not in ('DE', 'AT', 'CH')");
        }
        else
        {
            data = [];
            temp = pData;
        }

        for(var i = 0; i < data.length; i++)    
            data[i][1] = translateStr(data[i][1], pLanguage);
        for(let i = 0; i < temp.length; i++)    
            temp[i][1] = translateStr(temp[i][1], pLanguage).trim();

        //Sortierung nach der Übersetzung
        array_mDimSort(temp, 1, true, false);

        return data.concat(temp);
    }
    
    /**
     * Delivers the next unique number
     *
     * @param {String} pColumn req database column that contains unique numbers
     * @param {String} pTable req database table
     * @param {Number} pStartNumber opt number to start numeration
     * 
     * @result {String} next valid number
     */
    this.getNextUniqueNumber = function(pColumn, pTable, pStartNumber){
        var maxNum = that.getMaxUniqueNumber(pColumn, pTable);
        
        if(maxNum == "0")    
        {
            if(pStartNumber == undefined)   pStartNumber = 1000;
            return pStartNumber;
        }
        
        return eMath.addInt(maxNum, "1");//increment currently highest number
    }
    
    /**
     * Checks if the passed number is valid (has to be unique)
     * 
     * @param {String} pNumber number to check
     * @param {String} pColumn req database column that contains unique numbers
     * @param {String} pTable req database table
     * 
     * @result {boolean} passed number is valid
     */
    this.validateUniqueNumber = function(pNumber, pColumn, pTable){
        var maxNum = that.getMaxUniqueNumber(pColumn, pTable);
        
        return Number(pNumber) > Number(maxNum);
    }
    
    /**
     * Delivers the hightest number currently stored in database
     * 
     * @param {String} pColumn req database column that contains unique numbers
     * @param {String} pTable req database table
     * 
     * @result {String} hightest number
     */
    this.getMaxUniqueNumber = function(pColumn, pTable){
        var maxNum = db.cell("select max(" + pColumn + ") from " + pTable);
        
        return maxNum == "" ? "0" : maxNum;
    }
}
/**
 * Class containing utility functions for use with JSON
 * @class
 */
function JSONUtils()
{
    /**
     * A custom JSON.stringify() to
     * - keep the functions as string
     * - stringify JavaArrays
     * - stringify undefined as undefined and not as null
     *
     * @param {Object} pObj req The object to stringify
     *
     * @return {String} The stringified string
     * 
     * 
     */
    this.stringifyJSON = function(pObj)
    {
        //stringify part from JSON polyfill: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
        var toString = Object.prototype.toString;
        var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'};
        var escFunc = function (m) { return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1); };
        var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;

        var stringify = function (value){
          //because: "undefined == null" is true
          if (value === undefined) {
            return 'undefined';
          } else if (value == null) {
            return 'null';
          } else if (typeof value === 'number') {
            return isFinite(value) ? value.toString() : 'null';
          } else if (typeof value === 'boolean') {
            return value.toString();
          } else if (typeof value === 'object')
          {
              //check with "hasOwnProperty" because in JavaArrays using value.toJSON would cause an exception
             if (toString.call(value) != '[object Map]' && value.hasOwnProperty("toJSON") && typeof value.toJSON === 'function') {
              return value.toString();
            } else if (toString.call(value) === '[object Array]') {
              var res = '[';
              for (var i = 0; i < value.length; i++)
                res += (i ? ', ' : '') + stringify(value[i]);
              return res + ']';
            } else if (toString.call(value) === '[object Object]' || toString.call(value) === '[object Map]') {
              var tmp = [];
              for (var k in value) {
                if (hasOwnProperty.call(value, k))
                  tmp.push(stringify(k) + ': ' + stringify(value[k]));
              }
              return '{' + tmp.join(', ') + '}';
            //custom addition to stringify Rhino JavaArrays
            } else if (toString.call(value) === '[object JavaArray]') {
                return value.toSource().toString();
            }
          }
          //custom addition for function transform
          //JSON.stringify returns null on functions (default)
          //instead return source code of funciton for callback-functions
          else if (typeof value === 'function')
          {
              return value.toString();
          }

          return '"' + value.toString().replace(escRE, escFunc) + '"';
        }

        return stringify(pObj);
    }
    /**
     * checks if pStr is JSON
     *
     * @param {pStr}
     *
     * @return false or parsed JSON
     * 
     * 
     */
    this.isJSON = function(pStr)
    {
        if (typeof(pStr) != "string")
            return false;

        if (pStr[0] != "{")
            return false;

        if (pStr[pStr.length-1] != "}")
            return false;

        try
        {
            var parse = JSON.parse(pStr);
            return parse;
        }
        catch (ex)
        {
            return false;
        }

        return parse;
    }    
}

/**
 * Class containing utility functions for copying modules
 * @class
 */
function CopyModuleUtils()
{
    var that = this;
    
    /**
     * opens the new created modules in neonClient
     * 
     * @param {String} pNeonContext req Name of the neon context that should be opened
     * @param {Object} pModulesMapping req ModulesMapping object created by method copyModule
     * 
     * @example var ModulesMapping = CMUtils.copyModule(InputMapping);
     *
     *   CMUtils.openNewModules("Offer_context", ModulesMapping);
     */
    this.openNewModules = function(pNeonContext, pModulesMapping)
    {
        if(pModulesMapping != undefined)
        {
            var rootModule = Object.keys(pModulesMapping)[0];
            if(pModulesMapping[rootModule].DataRows != undefined)
            {
                var newids = [];

                for(var row in pModulesMapping[rootModule].DataRows)
                {
                    newids.push(pModulesMapping[rootModule].DataRows[row].newPrimaryKey);
                }

                if(newids.length > 0)
                    neon.openContext(pNeonContext, newids, neon.OPERATINGSTATE_VIEW, null);
            }
        }
    }
    
   /**
    *   Creates a copy of a specified module together with specified subordinated modules. <br>
    *   The structure of the input mapping object is the following:  <br>
    *  pInputMapping {
    *   (only one rootModule allowed)
    *    $rootModule$: { 
    *        condition: "sqlWhereCondition"
    *        , ValueMapping: {$colName$: "value"}
    *        , destinationModuleName: "destinationModuleName"
    *        , DestinationColumnMapping: { $sourceColName$ : "destinationColName" }  
    *        , SubModules: {
    *            $Module$:{ 
    *                condition: "sqlWhereCondition"
    *                , ValueMapping: {$colName$: "value"}
    *                , destinationModuleName: "destinationModuleName"
    *                , DestinationColumnMapping: { $sourceColName$ : "destinationColName" }  
    *                , SubModules: {...}
    *            }
    *        }
    *    }
    * }
    * 
    * @param {Object} pInputMapping
    *
    * @example var CMUtils = new CopyModuleUtils();
    *
    *    var InputMapping = {
    *
    *        "OFFER": {
    *                condition: "OFFERID = '" + vars.get("$field.OFFERID") + "'"
    *                ,SubModules:{
    *                    "OFFERITEM": {
    *                        condition: "OFFER_ID = '" + vars.get("$field.OFFERID") + "' order by ITEMSORT" 
    *                }
    *            }
    *        }
    *    }
    *
    *    CMUtils.copyModule(InputMapping);
    */
    this.copyModule = function(pInputMapping)
    {
        var AliasDefinitionStructure = project.getAliasDefinitionStructure("Data_alias", null);
        var ModulesMapping = {};
        var statements = [];

        buildMapping( pInputMapping );
        buildStatements( ModulesMapping );
        
        if(statements.length > 0)
            db.inserts( statements ); 
        
        return ModulesMapping;
        
        /**
         * Builds a mapping Object for the copyModule Method.  <br>
         * The structure of the Object is the following:  <br>
         * ModulesMapping = {  
         * 
         *   $rootModule$: { ($$ marks an object property) 
         *   
         *        (ModuleMapping) 
         *        name: "moduleName"  
         *        , destinationModuleName: "destinationModuleName" 
         *        , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } 
         *        , ValueMapping: {$colName$: "value"} 
         *        , dataRows:{ 
         *            (ModuleRowMapping) 
         *            $rowid$: {  
         *                name: "moduleName" 
         *                , oldPrimaryKey: "oldPrimaryKeyValue" 
         *                , newPrimaryKey: "newPrimaryKeyValue" 
         *                , ColumnMapping: { $colName$: { newValue: "newValue", oldValue: "oldValue", destinationColumn: "destinationColumn" } } 
         *                , ModuleMapping: object reference to ModuleMapping object that contains ModuleRowMapping objects 
         *                , ParentModuleMapping: object reference to supervised ModuleMapping object (at this point "null" because there is no supervised object) 
         *            } 
         *        }   
         *        , SubModules: { 
         *                  (ModuleMapping) 
         *                  $moduleName$: { 
         *                       name: "moduleName" 
         *                      , destinationModuleName: "destinationModuleName" 
         *                      , DestinationColumnMapping: { $sourceColName$ : "destinationColName" } 
         *                      , ValueMapping: {$colName$: "value"} 
         *                      , dataRows:{ 
         *                          (ModuleRowMapping) 
         *                           $rowid$: {  
         *                              name: "moduleName" 
         *                              , oldPrimaryKey: "oldPrimaryKeyValue" 
         *                              , newPrimaryKey: "newPrimaryKeyValue" 
         *                              , ColumnMapping: { $colName$: { newValue: "newValue", oldValue: "oldValue", destinationColumn: "destinationColumn" } } 
         *                              , ModuleMapping:  
         *                              , ParentModuleMapping: object reference to supervised ModuleMapping object (at this point "null" because there is no supervised object) 
         *                           } 
         *                      } 
         *                      , SubModules: {...} 
         *                  } 
         *        }  
         *
         *   } 
         *
         *} 
         *
         * @param {Object} pInputMapping InputMapping
         */
        function buildMapping(pInputMapping)
        {
            //root mapping
            var rootModule = Object.keys(pInputMapping)[0];
            var ModuleMapping = _ModuleMapping(rootModule, pInputMapping[rootModule]);
            var ModuleData = _getModuleData(rootModule, pInputMapping[rootModule].condition);
            
            for(var row in ModuleData)
            {
                var ModuleRowMapping = _ModuleRowMapping(ModuleMapping, null, ModuleData[row]);

                ModuleMapping.DataRows[ModuleRowMapping.oldPrimaryKey] = ModuleRowMapping;
            }
            
            ModulesMapping[rootModule] = ModuleMapping;
            
            //recursive subordinated modules mapping
            _buildSubordinatedMapping(pInputMapping, ModuleMapping);
            
            
            //delivers stored data for module in Database with condition
            function _getModuleData(pModule, pCondition)
            {
                if(pModule == undefined)    return {};
                
                var ModuleColumnsStructure = AliasDefinitionStructure.tables[pModule].columns;
                var cols = Object.keys(ModuleColumnsStructure);
                
                var condition = "1=1";
                if(pCondition != undefined)
                    condition = pCondition;

                var dbData = db.table("select " + cols.join(", ") + " from " + pModule + " where " + condition);
                
                //map 2d-Array to Object { $rowNumber$: { $columnName$: { value: "valueInDB" } } }
                var DataObj = {};
                for(var row = 0; row < dbData.length; row++)
                {
                    DataObj[row] = {};
                    for(var col = 0; col < dbData[row].length; col++)
                    {
                        DataObj[row][cols[col]] = {
                            value: dbData[row][col]
                        };
                    }
                }

                return DataObj;
            }
            
            
            //recursive: ModuleMapping and ModuleRowMapping for subordinated modules
            function _buildSubordinatedMapping(pInputMapping, pParentModuleMapping)
            {
                var SubModules = pInputMapping[pParentModuleMapping.name].SubModules;
                if(SubModules == undefined)
                    return;

                for(var subModuleName in SubModules)
                {
                    var ModuleMapping = _ModuleMapping(subModuleName, SubModules[subModuleName]);
                    ModuleData = _getModuleData(subModuleName, SubModules[subModuleName].condition);
                    for(var row in ModuleData)
                    {
                        ModuleRowMapping = _ModuleRowMapping(ModuleMapping, pParentModuleMapping, ModuleData[row]);
                        ModuleMapping.DataRows[ModuleRowMapping.oldPrimaryKey] = ModuleRowMapping;
                    }

                    ModulesMapping[pParentModuleMapping.name].SubModules[subModuleName] = ModuleMapping;

                    _buildSubordinatedMapping(SubModules, ModuleMapping);
                }
            }
            
            function _ModuleMapping( pModuleName, pInputModuleMapping )
            {
                return {
                    name: pModuleName,
                    destinationModuleName: pInputModuleMapping.destinationModuleName == undefined ? pModuleName : pInputModuleMapping.destinationModuleName,
                    DestinationColumnMapping: pInputModuleMapping.DestinationColumnMapping == undefined ? {} : pInputModuleMapping.DestinationColumnMapping,
                    ValueMapping: pInputModuleMapping.ValueMapping == undefined ? {} : pInputModuleMapping.ValueMapping,
                    DataRows:{}, 
                    SubModules: {}
                };
            }

            function _ModuleRowMapping(pModuleMapping, pParentModuleMapping, pDataRow)
            {
                var ModuleRowMapping = {
                    name: pModuleMapping.name,
                    oldPrimaryKey: null,
                    newPrimaryKey: null,
                    ColumnMapping: {},
                    ModuleMapping: pModuleMapping,
                    ParentModuleMapping: pParentModuleMapping
                };

                var ModuleColumnsStructure = AliasDefinitionStructure.tables[ModuleRowMapping.name].columns;

               //build ColumnMapping
                for(var col in ModuleColumnsStructure)
                {
                    //set defined columns from InputMapping -> if not defined, use the same column
                    var destinationColumn = ModuleRowMapping.ModuleMapping.DestinationColumnMapping[col];
                    if(destinationColumn == undefined)
                        destinationColumn = col; 
                    
                    //set defined values from InputMapping -> if not defined, use the value from DB
                    var oldValue = pDataRow[col].value;
                    var newValue = newValue = ModuleRowMapping.ModuleMapping.ValueMapping[col];
                    if(newValue == undefined)
                        newValue = oldValue;
                    
                    //set new primary key
                    if(ModuleColumnsStructure[col].primaryKey)  
                    {
                        ModuleRowMapping.oldPrimaryKey = ModuleRowMapping.ColumnMapping[col] = oldValue;
                        newValue = util.getNewUUID();
                        ModuleRowMapping.newPrimaryKey = newValue;
                    }
                    
                    ModuleRowMapping.ColumnMapping[col] = _columnMapping(newValue, oldValue, destinationColumn);
                }

                switch(ModuleRowMapping.name)
                {
                    case "OFFER":
                    {
                        //andere Values setzen
                        var offUtils = new OfferUtils();
                        var dtUtils = new DateUtils();
                        
                        ModuleRowMapping.ColumnMapping["OFFERCODE"].newValue = offUtils.getNextOfferNumber();
                        ModuleRowMapping.ColumnMapping["OFFERDATE"].newValue = dtUtils.getTodayUTC();
                    }
                    break;
                    case "OFFERITEM":
                    {
                        //OFFER_ID mappen
                        if(ModuleRowMapping.ParentModuleMapping.name == "OFFER")
                        {
                            ModuleRowMapping.ColumnMapping["OFFER_ID"].newValue = ModulesMapping[ModuleRowMapping.ParentModuleMapping.name].DataRows[ModuleRowMapping.ColumnMapping["OFFER_ID"].oldValue].newPrimaryKey;
                        }
                        //ASSIGNEDTO mappen
                        if(ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue != "")
                        {
                            if(ModuleRowMapping.ParentModuleMapping == null)
                            {
                                ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].newValue = ModulesMapping["OFFERITEM"].DataRows[ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue].newPrimaryKey;
                            }
                            else
                            {
                                ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].newValue = ModuleRowMapping.ModuleMapping.DataRows[ModuleRowMapping.ColumnMapping["ASSIGNEDTO"].oldValue].newPrimaryKey;
                            }
                        }
                    }
                    break;
                    case "OFFERLINK":
                    {

                    }
                    default:
                    {

                    }


                }

                //Spezialbehandlung USER_NEW DATENEW....
                if(ModuleRowMapping.ColumnMapping["DATE_NEW"] != undefined)        ModuleRowMapping.ColumnMapping["DATE_NEW"].newValue = datetime.date();
                if(ModuleRowMapping.ColumnMapping["USER_NEW"] != undefined)        ModuleRowMapping.ColumnMapping["USER_NEW"].newValue = vars.get("$sys.user");
                if(ModuleRowMapping.ColumnMapping["DATE_EDIT"] != undefined)        ModuleRowMapping.ColumnMapping["DATE_EDIT"].newValue = "";
                if(ModuleRowMapping.ColumnMapping["USER_EDIT"] != undefined)        ModuleRowMapping.ColumnMapping["USER_EDIT"].newValue = "";

                return ModuleRowMapping;
            }
            
            
            
            
            function _columnMapping(pNewValue, pOldValue, pDestinationColumn)
            {
                return {
                    newValue: pNewValue,
                    oldValue: pOldValue,
                    destinationColumn: pDestinationColumn
                };
            }

        }
        
        /**
         * Builds the insert statements for passed ModulesMapping
         * 
         * @param {Object} pModulesMapping ModulesMapping from buildMapping()
         */
        function buildStatements(pModulesMapping)
        {   
            var rootModule = Object.keys(pModulesMapping)[0];

            for(var row in pModulesMapping[rootModule].DataRows)
            {
                //buildInsertStatement
                statements.push(_statement(pModulesMapping[rootModule].DataRows[row]));
            }

            _subordinatedStatements(pModulesMapping[rootModule]);

            function _subordinatedStatements(pMapping)
            {
                if(pMapping.SubModules == undefined)
                    return;

                for(var subModule in pMapping.SubModules)
                {

                    for(var row in pMapping.SubModules[subModule].DataRows)
                    {
                        statements.push(_statement(pMapping.SubModules[subModule].DataRows[row]));
                    }

                    _subordinatedStatements(pMapping.SubModules[subModule]);
                }

            }

            function _statement(pRowMapping)
            {
                var cols = [];
                var vals = [];
                var destTable = pRowMapping.ModuleMapping.destinationModuleName;
                var colMapping = pRowMapping.ColumnMapping;

                for(var col in colMapping)
                {
                    cols.push(colMapping[col].destinationColumn);
                    vals.push(colMapping[col].newValue.toString());
                }

                var colTypes = db.getColumnTypes(destTable, cols)

                return [destTable, cols, colTypes, vals];
            }
        }
    }
}

/**
 * provides somehow static methods for special handling in JDito-Processes
 * do not create an instance of this
 */
function ProcessHandlingUtil()
{
}

/**
 * In onValidation-Process a local variable called "$local.value" is made available from kernel. 
 * It contains the entered value - the field contains the value only after successfull validation (vars.get("$field.Fieldname")).
 * The onValidation-Process is running again before saving the entity, at this point there's "$local.value" varialbe no longer available,
 * but the entered value now is the present one because the field has already been validated before.
 * Otherwise a "variable not found" error would occur.
 * 
 * @param {String} pFieldValue req value of the field onValidation-Process is executed ( e.g. vars.get("$field.Fieldname") )
 * @return {String} Field value for onValidation-Process
 */
ProcessHandlingUtil.getOnValidationValue = function(pFieldValue)
{
    return vars.exists("$local.value") ? vars.get("$local.value") : pFieldValue;
}