//Browse Local File
//#BrowseFile #FileDialog #DialogBox #File
(function () {
    _UND = "undefined";
    _d = (typeof _d == "undefined") ? document : _d;
    _UND = (typeof _UND == 'undefined') ? 'undefined' : _UND;
    //function to replace string
    var _Replace = function (strSearch, strReplace, str) {
        var newstr = "";
        if (_Trim(str) != "") {
            var strarr = str.split(strSearch);
            newstr = strarr.join(strReplace);
        }
        return newstr;
    }

    String.prototype.Replace = function (strSearch, strReplace) { return _Replace(strSearch, strReplace, this.toString()); }
    _ = function (selector) {
        //by id (baseobj if set, not allowed, only document)
        var qsel = document.getElementById(selector);
        if (qsel != null) return qsel;

        //by classname
        var qsel = document.getElementsByClassName(selector);
        if (qsel.length > 0) {
            var arrRtn = Array.prototype.slice.call(qsel);
            return arrRtn.length == 1 ? arrRtn[0] : arrRtn;
        }

        //by name (baseobj if set, not allowed, only document)
        var qsel = document.getElementsByName(selector);
        if (qsel.length > 0) {
            var arrRtn = Array.prototype.slice.call(qsel);
            return arrRtn.length == 1 ? arrRtn[0] : arrRtn;
        }
        try {
            var qsel = document.querySelectorAll(selector);
            if (qsel == null || qsel.length < 1) {

            } else {
                var arrRtn = Array.prototype.slice.call(qsel);
                return arrRtn.length == 1 ? arrRtn[0] : arrRtn;
            }
        } catch (e) {

        }



        //if not found
        return null;
    }

    /* //trim leading and trailing space
    _Trim = function(val){
        //alert(val);
        if(typeof val == 'string'){
         //return val.replace(/^\s+|\s+$/g,'');
         if (typeof String.prototype.trim != 'function')return val.replace(/^\s+/, '').replace(/\s+$/, '');
             return val.trim();
        }else if(IsObject(val)){
        val = val.GetText();
        return _Trim(val);	
        }else if(IsArray(val)){
            var newarr = new Array();
            for(var s=0; s < val.length ; s++){
                var rtn = _Trim(val[s]);
                if(IsArray(rtn)){
                    newarr.concat(rtn);
                }else if(typeof rtn == 'string'){
                    newarr.push(rtn);
                }
            }
            return newarr;
        }else{
            return val;	
        }
    }
    String.prototype.Trim = function(n){return _Trim(this.toString())}
    Array.prototype.Trim = function(n){return _Trim(this)} */
    //Object.prototype.Trim = function(n){return _Trim(this)}

    _DataStrToArray = function (str, order) {
        order = typeof order == _UND ? false : order;
        var rtArr = {};
        var orderarr = new Array();
        var strarr = str.split("&");
        if (strarr.length > 0) {
            for (var s = 0; s < strarr.length; s++) {
                var atrrVal = strarr[s].split("=");
                if (atrrVal.length == 2) {
                    //atrrVal[1]=atrrVal[1].Replace("%2A","*").Replace("%2B","+").Replace("%2F","/").Replace("%40","@");
                    rtArr[atrrVal[0]] = rawunescape(atrrVal[1]);
                    if (order) orderarr[s] = atrrVal[0];
                }

            }

        }
        if (order) {
            return { NewArray: rtArr, OrderArray: orderarr }
        } else {
            return rtArr;
        }

    }

    function _ArrayToString(cssarr) {
        var cssStr = "";
        if (typeof cssarr != "object") return "";
        var cssarrarr = Object.entries(cssarr);
        for (const [key, value] of cssarrarr) {
            //if (cssarr.hasOwnProperty(key)) {
            //var element = cssarr[key];
            cssStr += key + "=" + encodeURIComponent(value) + "&";
            //}
        }

        /* for(var atr in cssarr){
            if(IsString(cssarr[atr])){
                 cssStr +=  atr + "=" + cssarr[atr] + "&"; 
            }
        } */
        if (_Trim(cssStr) == "") return "";
        cssStr = cssStr.substr(0, cssStr.length - 1);
        return cssStr;
    }

    rawescape = function (str) {
        var esstr = escape(str);
        esstr = esstr.Replace("*", "%2A").Replace("+", "%2B").Replace("/", "%2F").Replace("@", "%40");
        return esstr;
    }

    rawunescape = function (str) {
        str = str.Replace("%2A", "*").Replace("%2B", "+").Replace("%2F", "/").Replace("%40", "@").Replace("%C2%A0", " ");//%C2%A0
        return unescape(str);
    }

    //The Aim Ajax Engine Logic
    //#Ajax
    Ajax = function (timeout) {
        this.Timeout = timeout || 0;
        //initialize the XMLHTTPRequest Object
        this.req = (function () {
            var mainreq = null;
            if (window.XMLHttpRequest) {
                mainreq = new window.XMLHttpRequest
            } else {
                if (window.ActiveXObject) {
                    try {
                        mainreq = new ActiveXObject("Msxml2.XMLHTTP");
                    } catch (err1) {
                        try {
                            mainreq = new ActiveXObject("Microsoft.XMLHTTP");
                        } catch (err2) {

                            return false;
                        }
                    }
                }


            }
            // mainreq.Owner = this;
            return mainreq;
        })();

        this.req.Owner = this;

        //initialize the user defined parameters
        this.UserHandleProgress = null; //when sending/receiving from server
        this.UserCompleteHandler = null;//when operation completed
        this.UserErrorHandler = null; //when error occur
        this.UserAbortHandler = null; //when operation aborted by user

        //initialize return parameter to user handler
        this.SendData = {};//will hold the data sent to Ajax Post object
        this.URL = "";

        //The main ajax post object (HTML 5)
        //#Post #Ajax.Post
        this.Post = function (param) {

            //if no user parameter sent
            if (typeof param == _UND) {
                console.log("Aim Ajax Failed: No Parameter provided for Aim Ajax Object");
                return;
            }
            //if no server script sent
            if (typeof param.Action == _UND) {
                console.log("Aim Ajax Failed: No Action Script provided for Aim Ajax Object");
                return;
            }
            //if cannot create httpxmlrequest
            if (!this.req) {
                // alert(this.req)
                console.log('Aim Ajax Failed: Unable to create required object');
                return;
            }

            //instantiate a new FormData Object to package all user sent post data.
            var formdataObj = new FormData();
            //alert(typeof param.Data);
            //if no post data sent 
            if (typeof param.Data == _UND || typeof param.Data == "function") {
                //alert(JSON.stringify(param));
                param.Data = typeof param.PostData == _UND ? {} : param.PostData;
            }
            var dataarr = param.Data;
            //if is data string, convert to array
            if (_IsString(param.Data)) {
                dataarr = _DataStrToArray(param.Data);
            }

            //convert to array for optimization
            dataarrn = Object.entries(dataarr)

            //loop through individual data supplied
            for (const [key, value] of dataarrn) {
                //console.log(key+" - "+value);
                //if (dataarr.hasOwnProperty(key)) {
                //var value = dataarr[key];
                //alert(key + " = " + value);
                //add to the SendData Ajax object, which will be send back to user handlers
                this.SendData[key] = value;
                //if value is "?" (meaning a file is sent)
                //the key represent the file element id
                if (value == "?") { //if file
                    //alert('File');
                    //loop through all files of the file element and append it to the filedata object 
                    //console.log(key);                       
                    var file = _(key);
                    if (file.files.length > 0) {
                        if (file.files.length == 1) {//if one item
                            // alert(file.files[0]);
                            formdataObj.append(key, file.files[0]);
                        } else {
                            for (var fi = 0; fi < file.files.length; fi++) {
                                formdataObj.append(key + "[]", file.files[fi]);
                            }
                        }
                    }

                } else if (value == "??") { //if camera Image (blob)
                    //the key represent the Camera Photo blob key
                    //get the photo
                    if (typeof _Camera.Photos[key] != "undefined") {
                        console.log("Camera Photo added - " + key);
                        formdataObj.append(key, _Camera.Photos[key]);
                    }

                } else {
                    formdataObj.append(key, value);
                }
            }
            //}




            //set user define handlers
            //OnProgress
            if (typeof param.OnProgress != _UND) { this.UserHandleProgress = param.OnProgress; }
            //OnComplete
            if (typeof param.OnComplete != _UND) { this.UserCompleteHandler = param.OnComplete; }
            //OnError
            if (typeof param.OnError != _UND) { this.UserErrorHandler = param.OnError; }
            //OnAbort
            if (typeof param.OnAbort != _UND) { this.UserAbortHandler = param.OnAbort; }

            if (this.UserHandleProgress != null) {
                this.UserHandleProgress(0, param.Action, dataarr); //make sure it start loading
            }

            //set the xhtmlrequest event handlers (internal Handler - it checks if user handler exist else use default)
            //onprogress
            this.req.upload.addEventListener("progress", this.HandleProgress, false);
            this.req.upload.Owner = this.req; //keep the owner inside it
            //obload
            this.req.addEventListener("load", this.HandleComplete, false);
            //onerror
            this.req.addEventListener("error", this.HandleError, false);
            //onabort
            this.req.addEventListener("abort", this.HandleAbort, false);

            //the server script
            this.URL = param.Action;
            //the open conection with server
            this.req.open("POST", param.Action);
            //alert(JSON.stringify(formdataObj));
            //send post data
            this.req.send(formdataObj);
            //return the ajax objectF
            return this;
        }
        //function to handle progress
        this.HandleProgress = function (event) {
            var own = event.target.Owner.Owner
            // alert(own);
            if (own.UserHandleProgress != null) own.UserHandleProgress(event.loaded / event.total, own.URL, own.SendData);
        }
        //handle complete operation
        this.HandleComplete = function (event) {
            var own = event.target.Owner;
            //alert(own.SendData);
            if (own.UserCompleteHandler != null) own.UserCompleteHandler(event.target.responseText, own.URL, own.SendData);
            //event.target.Owner.UserCompleteHandler = null;
        }
        //handle error
        this.HandleError = function (event) {
            var own = event.target.Owner;
            if (own.UserErrorHandler != null) own.UserErrorHandler(event.target.statusText, own.URL, own.SendData);
        }
        //handle abort
        this.HandleAbort = function (event) {
            var own = event.target.Owner;
            if (own.UserAbortHandler != null) own.UserAbortHandler(event.target.statusText, own.URL, own.SendData);
        }
        //Abort the request
        this.Abort = function () {
            if (this.req) {
                this.req.onreadystatechange = function () { };
                this.req.abort();
                //this.req = null;
            }

        },

            this.abort = function () {
                if (this.req) {
                    this.req.onreadystatechange = function () { };
                    this.req.abort();
                    //this.req = null;
                }

            },

            //Backward compatibility
            this.PostResponse = function (Data, Script, OnComplete, type, OnError) {
                type = type || "text";
                OnError = OnError || function () { };
                return this.Post({
                    Action: Script,
                    Data: Data,
                    OnComplete: OnComplete,
                    OnError: OnError
                });
            }

    }

    AJAX = Ajax;
    //alert('Ajax');
    TL_AllAjaxObj = [];
    _RequestFrame = window.requestAnimationFrame
        || window.mozRequestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.msRequestAnimationFrame
        || function (f) { return setTimeout(f, 1000 / 60) }

    _CancelFrame = window.cancelAnimationFrame
        || window.mozCancelAnimationFrame
        || window.webkitCancelAnimationFrame
        || window.msCancelAnimationFrame
        || function (requestID) { clearTimeout(requestID) }
    //global variable to hold each file Index
    GFilesParm = [];
    //hold the template string which can be filled with data using placeholder to identify data position in the template string
    //#Template
    Template = {
        //#Template.Add
        Add: function (TID, TemplateStr) {
            //TID - the key of the template string in Aim.Engine.Template object
            //TemplateStr - string containing placeholder(optional) to be filled
            //NB - if TIB alredy exist in _.Template, will overwrite
            if ("" != _Trim(TemplateStr)) Template[TID] = TemplateStr;
        },
        //Fill the placeholders of the template string with data
        //#Template.Fill
        Fill: function (TID, FillData) {
            //TID - the key of the template string in Aim.Engine.Template object
            //FillData - the object containing key=>data to fill, (key-placeholder name, data - data to replace)
            if (typeof Template[TID] == "undefined") return ""; //if no such template exist return empty string
            var rtn = Template[TID];
            if (_IsObject(FillData)) { //if filldata is an object
                var objkeys = Object.keys(FillData);
                for (const key of objkeys) { //loop through all the data and replace in template string, searching using the filldata key and replace placeholder
                    //NB- placeholder are enclosed with {{placeholder}}

                    //if (FillData.hasOwnProperty(key)) {
                    var element = FillData[key].toString();
                    //replace placeholder with data
                    rtn = _Replace('{{' + key + '}}', element, rtn);
                    //}
                }
                //when done filling all data return the resultant filled string
                return rtn;
            } else if (_IsArray(FillData)) { //if an array of filldata sent
                //maening template need to be fill repeatedly e.g (loading a select element with option htmls)
                var arrrtn = "";
                for (var ss = 0; ss < FillData.length; ss++) { //loop through all fill data
                    //perform filling operation by calling same function, the form a comprehensive fill string
                    arrrtn += Template.Fill(TID, FillData[ss]);
                }
                return arrrtn;
            } else { //if filldata is not a valid type return template string
                return Template[TID];
            }
        }
    }

    //Walk moves through all childrem of a particular object
    //#Walker
    _Walker = function (type, elemarr, funct, passval) {
        //Type - the operation type, 1->Walk, 2-Each
        //The type specify how parameter will be sent to the loop function
        //Type 1(Walk) - index/name/key, value/object/element, user parameter
        //Type 2(Each) - value/object/element, user parameter, index/name/key
        //Type 3(Loop) - { Item => value/object/element, 
        //                 Key => index/name/key,
        //                    Param =  user parameter
        //                     }
        //elemarr - the object/element/String to walk through
        //funct - the function to run on each item of the el
        //passval is a parameter that will be passed into the funct function while calling it (optional)
        // passval = typeof passval == _UND?window:passval;
        //passval.func = funct;
        var runFunc = function (t, f, k, v, p) {
            //Run the iteration function based on the signature type
            if (t == 2) { //Each operation
                f(v, p, k);
            } else if (t == 3) { //Loop Operation
                f({ "Item": v, "Key": k, "Param": p });
            } else { //Walk Operation
                f(k, v, p);
            }
        }
        type = typeof type == _UND ? 1 : type;
        if (_IsNull(elemarr)) return;
        if (_IsObject(elemarr)) { //if elemarr is an object  and it has children pass the current child into the supplied function (funct)
            //console.log(elemarr);
            if (typeof elemarr.childNodes != _UND && typeof elemarr.childNodes.length != "undefined") {
                for (var i = 0; i < elemarr.childNodes.length; i++) {
                    runFunc(type, funct, i, childNodes[i], passval);
                    //funct(i,elemarr.childNodes[i],passval); 
                }
            } else {
                return;
            }
        } else if (_IsArray(elemarr)) { //if elemarr is an array pass the each item of the array to the supplied function;
            for (var i = 0; i < elemarr.length; i++) {
                runFunc(type, funct, i, elemarr[i], passval);
                //funct(i,elemarr[i],passval);
            }
        } else if (_IsString(elemarr)) { //if elemarr is string pass each character
            for (var i = 0; i < elemarr.length; i++) {
                runFunc(type, funct, i, elemarr.charAt(i), passval);
                //funct(i,elemarr.charAt(i),passval);
            }
        } else {
            return;
        }
    }

    _Walk = function (elemarr, funct, passval) {
        //elemarr => the array/object/element to loop through - Required
        //funct => the function to perform on each item of the array/object/element
        //paassval => holds value that will be passed to the function, hense can be used in function for all items of the array/element/object
        //NB: the signature of argument recieved by funct is
        //funct(key,item,passval)
        _Walker(1, elemarr, funct, passval);
    }

    _Money = (num) => {
        num = num + "";
        rtnval = num.Replace(",", "").ToNumber().toLocaleString(undefined, { maximumFractionDigits: 2 });
        rtnarr = rtnval.split(".");
        return rtnarr.length > 1 ? rtnval : rtnval + ".00";
    }


    //Globalized (elemarr.Each(funct,passval))
    //#_.Each
    _Each = function (elemarr, funct, passval) {
        //Walk description applicable
        //NB: the signature of argument recieved by funct is
        //funct(item,passval,key)
        _Walker(2, elemarr, funct, passval);
    },

        //Globalized (elemarr.Each(funct,passval))
        //#_.Loop
        _Loop = function (elemarr, funct, passval) {
            //Walk description applicable
            //NB: the signature of argument recieved by funct is
            //funct({Item:item,Key:key,Param:passval})
            _Walker(3, elemarr, funct, passval);
        },

        //Internal Function to perform string trimimg operation
        //(LeftTrim, RightTrim, Character Trim e.t.c)
        //-----------------------------------------------------
        //#StrTrimDir
        _StrTrimDir = function (val, n, dir) {
            //val - represent the object to trim (string,number,object,DOM object) (Array allowed) - Required
            //n - represent the number of character or string to trim (Array allowed)
            //NB - if n not set, trim spaces
            //dir - represent the trim direction i.e right - r or left - l or both - b

            n = typeof n == _UND ? null : n;
            dir = typeof dir == _UND ? 'b' : dir;
            if (_IsString(val)) { //if is a string
                if (_IsNull(n)) { //if n is null i.e no number of trim item sent, trim spaces only
                    if (dir == "r") { //if direction is right
                        return val.replace(/\s+$/g, ''); //trim all leading(right) white spaces
                    } else if (dir == "l") { //if direction is left
                        return val.replace(/^\s+/g, ''); //trim all trailing(left) white spaces	
                    } else {
                        return val.replace(/^\s+|\s+$/g, '');
                    }
                }
                if (_IsNumber(n)) { //if n is sent and is number
                    if (val != null && val.length > n) { //if the string to trim is greater than the number of character to trim
                        if (dir == "r") { //if to trim right
                            val = val.substr(0, val.length - n); //trim n number of character from the right
                        } else if (dir == "l") {//if to trim left characters
                            val = val.substr(n);
                            //val = val.substr(n,val.length-n); 
                        } else {//if to trim both left and right characters
                            if (val.length > n * 2) { //if the total character length if greater than the total removable character (n X 2)
                                val = val.substr(n, val.length - (n * 2));
                            } else {
                                //return empty string if the total removable character is greater than or equals to the character length, meaning all character are trimed
                                return "";
                            }
                        }
                    } else {//if the total number of character to trim is greater than the real string return empty string
                        val = "";
                    }
                    return val; //return the resultant string
                }
                if (_IsString(n)) { //if n  is a string trim the string
                    var strlen = n.length; // get the lenght of the string to trim off
                    if (strlen > val.length) return val; //if the trim lenght is greater than the real string, return back the string
                    var lststr_r = ""; //the right trimable character
                    var lststr_l = ""; //the left trimable character
                    if (dir == "r" || dir == "b") {//if to trim string from right or both
                        //get last part of the string
                        lststr_r = val.substr(val.length - strlen, strlen);
                    }
                    if (dir == "l" || dir == "b") {//if to trim from left or both
                        //get first part of the string
                        lststr_l = val.substr(0, strlen);
                    }

                    if ((dir == "r" || dir == "b") && lststr_r == n) { //if right trim or both and right trimable character is same as sent trim character
                        //return the trimed string
                        val = val.substr(0, val.length - strlen);
                    }

                    if ((dir == "l" || dir == "b") && lststr_l == n) {//if left trim or both and left trimable character is same as sent trim character
                        //return val.substr(strlen,val.length - strlen);
                        val = val.substr(strlen);
                    }

                    return val;
                }
                if (_IsArray(n)) { //if n is an array, meaning loop through all the item of n and use each item to run the trim function.
                    for (var s = 0; s < n.length; s++) {
                        val = _StrTrimDir(val, n[s], dir);//update val to the new trimed val
                    }
                    return val;
                }
                //if n is not any of the above checked type, just return val
                return val;
            } else if (_IsArray(val)) {//if val sent is an array, perform the trim on all elements of the array and return an array of trimed items
                var newarr = new Array();
                if (val.length > 0) {
                    for (var a = 0; a < val.length; a++) {
                        var strtrim = _StrTrimDir(val[a], n, dir);
                        //if an array returned after trim just concatinate it to the result array
                        if (_IsArray(strtrim)) newarr = newarr.concat(strtrim);
                        //if a string is returned after trim, add it to the result array
                        if (_IsString(strtrim)) newarr.push(strtrim);
                    }

                }
                return newarr;

            } else if (_IsObject(val)) { //if an object
                //perform trim on its text element (input - value, block elemet- textContent)
                return _StrTrimDir(_Text(val), n, dir);
            }

        }

    /* //trim leading and trailing space
    _Trim = function(val){
        //alert(val);
        if(typeof val == 'string'){
         //return val.replace(/^\s+|\s+$/g,'');
         if (typeof String.prototype.trim != 'function')return val.replace(/^\s+/, '').replace(/\s+$/, '');
         return val.trim();
        }else if(_IsObject(val)){
        val = val.GetText();
        return _Trim(val);	
        }else if(_IsArray(val)){
            var newarr = new Array();
            for(var s=0; s < val.length ; s++){
                var rtn = _Trim(val[s]);
                if(_IsArray(rtn)){
                    newarr.concat(rtn);
                }else if(typeof rtn == 'string'){
                    newarr.push(rtn);
                }
            }
            return newarr;
        }else{
          return val;	
        }
    } */

    //#_Trim #Trim
    _Trim = function (obj, n) { return _StrTrimDir(obj, n) }

    //trim leading characters
    //Globalized (obj.Trim(n))
    //#_TrimLeft #TrimLeft
    _TrimLeft = function (obj, n) { return _StrTrimDir(obj, n, 'l') }

    //trim trailing characters
    //Globalized (obj.Trim(n))
    //#_TrimRight #TrimRight
    _TrimRight = function (obj, n) { return _StrTrimDir(obj, n, 'r') }


    // String.prototype.Trim = function(n){return _Trim(this.toString())}
    // Array.prototype.Trim = function(n){return _Trim(this)}
    //Specific Function that checks type using the CheckType function
    CheckType = function (obj) {
        var tp = "";
        //obj - Holds the object/Element to Check Type - Required
        if (typeof obj == "undefined" || obj == null) return null; //if obj is null or not supplied return null
        //Check if obj is not a string and can have children 
        if (typeof obj != "string" && typeof obj.length != "undefined") {
            if (Array.isArray) { //if isArray is supported
                tp = Array.isArray(obj) ? 'array' : typeof obj;
            } else {
                try { //Check if it is an array, by running native array function on the Obj. if no error occur, return 'array' else return the type of the element/Argurment
                    var a = obj.sort();
                    var b = obj.slice(0);
                    tp = "array"
                } catch (e) {
                    tp = typeof obj;
                }
            }
        } else {
            tp = typeof obj;
        }
        //check if it is html element
        if (tp.toLowerCase() == "object") {
            tp = (typeof obj.tagName != "undefined") ? "element" : tp;
        }
        return tp;
    }

    _Text = function (objs, set, optionval) {
        //objs - the object to get its textContent or value (string - classname, id, tagname, css selector | object - DOM Object | array - array items could be string,object or both) - Required
        //set - if set, set the textContent or value(input) of the objs
        //optionval - determine if select option value or textContent is to be set or gotten
        //NB: false=>textContent (default); true=>value
        optionval = typeof optionval == "undefined" ? false : optionval;
        objs = _RP(objs);//get the element to referense the textContent or value

        if (_IsArray(objs)) {

            //if array of object found
            var newarr = new Array(); //this empty array will hold the value/textContent of each element
            for (var s = 0; s < objs.length; s++) { //loop through all the array object
                //if null just move to the next element
                if (_IsNull(objs[s])) { continue; }
                //run a recursive call
                var rtn = _Text(objs[s], set, optionval);
                //if array return just concatinate it
                if (_IsArray(rtn)) {
                    newarr = newarr.concat(rtn);
                } else { //else if just a string(textContent or value) add to the array
                    newarr.push(rtn);
                }
            }
            return newarr;
        } else if (_IsObject(objs)) { //if element is an object

            if (typeof objs.options != "undefined") {// if it is a select element
                //get the textContent of the selected option
                //NB - to get the selected option value, Use _.Value(---);

                if (!optionval) { //if select option textcontent is to be used
                    //if the text content is supply, i.e a set operation set it before returning
                    if (typeof (set) != "undefined" && set != null) {
                        //change the text Content of the Selected Option
                        objs.options[objs.selectedIndex].textContent = set.toString()
                    }
                    return typeof objs.options[objs.selectedIndex] != "undefined" ? objs.options[objs.selectedIndex].textContent : set;
                } else { //if select option value is to be used
                    if (typeof (set) != "undefined" && set != null) {
                        //change the text Content of the Selected Option
                        objs.value = set.toString()
                    }
                    return typeof objs.options[objs.selectedIndex] != "undefined" ? objs.options[objs.selectedIndex].value : set;
                }
            } else if (typeof (objs.value) != "undefined") { //if it is an input element or elment that has value attribute
                //console.log(objs.type);
                //if the value is supply, i.e a set operation set it befor returning
                if (typeof (set) != "undefined" && set != null) { objs.value = set.toString() }
                //if radio button or checkbox
                if (_IsElement(objs) && objs.tagName.toLowerCase() == "input" && (objs.type.toLowerCase() == "checkbox" || objs.type.toLowerCase() == "radio")) {

                    /*if(!objs.checked){ //if not selected
                        return null;
                    }*/
                    return objs.checked;
                }
                return objs.value;
            } else if (typeof (objs) != "undefined") {

                //if the textContent is supply, i.e a set operation set it befor returning
                if (typeof (set) != "undefined" && set != null) { objs.textContent = set.toString() }

                return objs.textContent
            }
        } else {
            return "";
        }

    }

    //Handle phone number validation
    //----------------------------------
    //#ChkPhoneNum
    _ChkPhoneNum = function (number, mindigit, maxdigit) {
        //number - the number to check its validity (Array Allowed)
        //NB - if number is an array, check if all the items of the array are valid phone number
        //NB - if number is DOM object, perform check on its textContent / value(input)
        //mindigit - minimum number of of digit expected - default => 6
        //maxdigit - maximum number of of digit expected - default => 15
        mindigit = typeof mindigit == _UND ? 6 : mindigit;
        maxdigit = typeof maxdigit == _UND ? 15 : maxdigit;

        if (_IsString(number)) { //if is sent as a string
            if ("" == _Trim(number)) return false;
            //trim out special phone prefix
            number = _TrimLeft(number, ["+", "-"]); //TrimLeft function trim leading character
            //Check if all character is a digit
            for (var s = 0; s < number.length; s++) { //loop through all characters
                var char = number[s]; //get current character
                //if(typeof parseInt(char) == "NaN"){
                if (isNaN(parseInt(char))) { //check if not an integer number
                    return false; //return false; meaning not a valid phone number
                }
                //}
            }
            //check if number is between 6 to 15 digit(Can be modified to cater for any range) - idea => it will be beter if it is dynamic user defined
            if (number.length >= mindigit && number.length <= maxdigit) {
                return true;
            } else {
                return false;
            }
        } else if (_IsNumber(number)) { //if argument sent is a numeric
            //convert to string and rerun the function on it
            return _ChkPhoneNum(number.toString(), mindigit, maxdigit);
        } else if (_IsArray(number)) { //if the number sent is an array of items
            if (0 == number.length) return false; //if array is empty return false
            for (var cp = 0; cp < number.length; cp++) { //loop through all item of the array and run reculsively.
                if (_ChkPhoneNum(number[cp], mindigit, maxdigit) == false) {
                    //if any of the array item is an invalid phone number return false
                    return false;
                }
            }
            //if no invalid phone number found, return true
            return true;
        } else { //if not any of the above tested type (assume is an object)
            var objtxt = _Text(number); //get the textContent or value as required
            if (objtxt == "") return false; //if notting gotten, return false (invalid)

            //Check if valid phone number and return as required
            return _ChkPhoneNum(objtxt, mindigit, maxdigit);
        }
    }

    //Handles Email Address Validation
    //-------------------------------------------
    //#ChkEmail
    _ChkEmail = function (obj) {
        //obj - holds the user sent email (string, object) (Array Allowed)

        if (_IsArray(obj)) {
            //if obj is an array, check if all the items of the array are valid email address
            for (var index = 0; index < obj.length; index++) {
                var element = obj[index];
                if (!_ChkEmail(element)) {
                    return false
                }
            }
            return true;
        } else if (_IsObject(obj)) {
            //if obj is an object, check if the textContent or value(input) is a valid email address
            return _ChkEmail(_Text(obj));
        } else if (_IsString(obj)) { //if obj is string
            if ("" == _Trim(obj)) return false; //if obj is empty return false
            var regexp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
            var test = regexp.test(obj);//test if sent email string matches the email regexp 
            return test;
        } else {
            return false;
        }


    }

    //The random object - help to generate random numbers , string Both
    //#Rand
    _Rand = {
        //generate random numbers
        Number: function (len) {
            len = typeof len == "undefined" ? 1 : isNaN(parseInt(len)) ? 1 : parseInt(len);
            return Math.floor(Math.random() * len);
        },
        //generate random strings
        String: function (len, charset) {
            len = typeof len == "undefined" ? 1 : isNaN(parseInt(len)) ? 1 : parseInt(len);
            var chr = "abcdefghijklmnopqrstuvwxyzQWERTYUIOPASDFGHJKLZXCVBNM";
            charset = typeof charset == "undefined" ? chr : charset;
            var str = "";
            for (var d = 0; d < len; d++) {
                var rnum = _Rand.Number(charset.length);
                str += charset.substr(rnum, 1);
            }
            return str;

        }
    }

    //----------------------------------------
    //_IsArray return true if obj is array 
    _IsArray = function (obj) { return (CheckType(obj) == "array") ? true : false; }
    //IsString return true if obj is string
    _IsString = function (obj) { return (CheckType(obj) == "string") ? true : false; }
    //IsNumber return true if obj is numeric
    _IsNumber = function (obj) { return (CheckType(obj) == "number") ? true : false; }
    //_IsObject return true if obj is object
    _IsObject = function (obj) { return (typeof obj == "object") ? true : false; }
    //IsFunction return true if obj is a functionG305
    _IsFunction = function (obj) { return (CheckType(obj) == "function") ? true : false; }
    //_IsNull return true if obj is null
    _IsNull = function (obj) { return (obj == null) ? true : false; }
    _IsElement = function (obj) { return (CheckType(obj) == "element") ? true : false; }
    _IsFound = function (obj) { return !_IsNull(obj) }
    //Function to get the id of an element, if no id set generarte and set new id for the element
    //#_GetId #ID
    _GetId = function (obj, id) {
        //obj => represent the element to get/set id
        obj = _IsString(obj) ? _(obj) : obj;
        //alert(typeof obj);
        //If array of object sent
        if (_IsArray(obj)) {

            var rst = [];
            for (var ddd = 0; ddd < obj.length; ddd++) {
                var oid = _GetId(obj[ddd], id);
                if (_IsArray(oid)) {
                    rst = rst.concat(oid);
                } else {
                    rst.push(oid);
                }
            }
            return rst;
        }

        //if an element
        if (_IsObject(obj)) {

            //if no id for the element
            if (typeof obj.id == "undefined" || _Trim(obj.id) == "") {
                var newid = typeof id == "undefined" || id == null || _Trim(id) == "" ? _Rand.String(20) : id;
                //alert(_IsFound(_('sjjsjsksks')));
                while (_IsFound(_(newid))) {
                    newid = _Rand.String(20);
                }
                obj.id = newid
            }
            return obj.id;
        }

        return "";


    }

    _RP = function (parameter) {
        if (typeof (parameter) == "object") {
            return parameter;
        } else if (typeof (parameter) == "string") {
            //return document.getElementById(parameter)
            return _(parameter);
        }
    }

    //Convert value to numeric type
    //#_ToNum #_ToNumber #ToInteger
    _ToNum = function (val, type) {
        // alert("Enter => "+val.toString());
        //val => the value to convert (Array Allowed) -Required
        //type => the numeric type to convert to
        //1=>float Number(default); 2=>Integer Numbers
        //NB: if convertion failed, 0 is returned
        type = typeof type == _UND ? 1 : type;

        if (_IsString(val)) { //if val is string, perform convertion
            // alert("String Found => "+val)
            var rst = type == 2 ? parseInt(val) : parseFloat(val);
            //console.log(rst);
            return (isNaN(rst)) ? 0 : rst;
        }

        //if array of value is supplied
        if (_IsArray(val)) {
            //alert("Array Found => "+ val.toString())
            var newarr = [];
            //loop through all array and form new array of numeric type
            for (var q = 0; q < val.length; q++) {
                // alert(q+" Item =>" + val[q].toString());
                var rval = _ToNum(val[q], type);
                if (_IsArray(rval)) {
                    // alert("Array Result Concat => " + rval.toString())
                    // alert("With => " + newarr.toString())
                    newarr = newarr.concat(rval);
                } else {
                    //alert("String Result Push => " + rval.toString())
                    newarr.push(rval);
                }
                //alert("Updated Array New => "+ newarr.toString())

            }
            //alert("Return Array New => "+ newarr.toString())
            return newarr;
        }

        if (_IsElement(val)) {
            var txt = _Text(val);
            if (_Trim(txt) != "") {
                return _ToNum(txt, type);
            } else {
                return 0;
            }
        }

        return _ToNum(val.toString(), type);

    }

    //Convert value to numeric type
    //Globalized (val.ToNumber(type))
    //#_.ToNumber
    _ToNumber = function (val, type) {
        //val => the value to convert (Array Allowed) -Required
        //type => the numeric type to convert to
        //1=>float Number(default); 2=>Integer Numbers
        //NB: if convertion failed, 0 is returned
        return _ToNum(val, type);
    },

        //Conver value to float
        //Globalized (val._ToFloat())
        //#_._ToFloat
        _ToFloat = function (val) {
            //_ToNumber Description Applicable
            return _ToNum(val);
        },

        //Conver value to float
        //Globalized (val.ToInteger())
        //#_ToInteger
        _ToInteger = function (val) {
            //_ToNumber Description Applicable
            return _ToNum(val, 2);
        }

    //Aim function for remove Rule in aim style sheet
    //#RemoveRule
    _RemoveRule = function (index) {

        if ("deleteRule" in Style.sheet) {
            //console.log(index);
            Style.sheet.deleteRule(index);
        }
        else if ("removeRule" in Style.sheet) {
            return Style.sheet.removeRule(index);
        }
    }

    _AddRule = function (selector, rules, index) {

        if ("insertRule" in Style.sheet) {
            index = typeof index == _UND ? Style.sheet.cssRules.length : index;
            //alert(selector + "{" + rules + "}");
            return Style.sheet.insertRule(selector + "{" + rules + "}", index);

        }
        else if ("addRule" in Style.sheet) {
            return Style.sheet.addRule(selector, rules, index);
        }
    }

    //merge two object
    _MergeObj = (destination, source) => {
        //alert(JSON.stringify(destination));
        var objkeys = Object.keys(source);
        for (const property of objkeys) {
            // if (source.hasOwnProperty(property)) {

            destination[property] = source[property];

            // }
        }
        // alert(JSON.stringify(destination));
        return destination;
    }

    //Convert obj to Array type
    //#ToArray
    _ToArray = function (obj, delimiter, innerdelimeter, keyType) {
        //obj => item to convert to array
        //delimiter => hold the string delimiter that joins onj items
        //innerdelimeter => delimiter that joins key and value of individual obj items
        //keyType => the type of key to return
        //0=>Asscociate key ; 1=>Numeric key; 2=>Both(default)
        delimiter = typeof delimiter == _UND ? "" : delimiter;
        innerdelimeter = typeof innerdelimeter == _UND ? "" : innerdelimeter;
        keyType = typeof keyType == _UND ? 2 : keyType;
        var newarr = [];
        if (obj == null) return newarr;

        //if obj is a string break individual characters to array
        if (_IsString(obj)) {
            if (_Trim(obj) == "") return [];
            if (delimiter == "") {
                for (var s = 0; s < obj.length; s++) {
                    newarr.push(obj[s]);
                }
            } else {
                //if split delimeter exist split string to form array
                var brarr = obj.split(delimiter);

                //if no key - value delimiter, return the formed array
                if (innerdelimeter == "") return brarr;

                //if items found
                if (brarr.length > 0) {
                    //loop through all items and break individual string to get key and value, using the innerdelimeter
                    for (var s = 0; s < brarr.length; s++) {
                        var indbr = brarr[s];

                        //split using inner delimeter to get key-value
                        var indbrsp = indbr.split(innerdelimeter);

                        //if key and value found
                        if (indbrsp.length < 2) {
                            indbrsp[1] = indbrsp[0]; //use the value as key
                        }
                        //Asscociate key
                        if (keyType == 0 || keyType == 2) {
                            //set the newarr to hold the value in the key location
                            newarr[indbrsp[0]] = indbrsp[1];
                        }

                        //Numeric key
                        if (keyType == 1 || keyType == 2) {
                            //set numeric location as well
                            newarr[s] = indbrsp[1];
                        }
                        //alert(newarr.length);
                    }
                    //insert the length, so that it can be assessible
                    newarr['length'] = brarr.length;
                }
            }
            //if number break individual digit to array
        } else if (_IsNumber(obj)) {

            /*obj = obj.toString();
            for(var s=0 ; s<obj.length; s++){
                var digit = obj[s];
                if(digit != "."){
                    digit = Number(digit);
                }
              newarr.push(digit);	
            }*/
            return _ToArray(obj.toString(), delimiter, innerdelimeter)

        } else if (_IsArray(obj)) {
            newarr = obj;
        } else { //other types
            newarr = Array.prototype.slice.call(obj);

        }


        return newarr;
    }

    //Convert Object, asscociate array to string
    _ToString = function (obj, arraydel, keyvaldel) {
        //obj - holds the array or accociate array  to convert
        //arraydel - hold the delimiter that joins each object items
        //keyvaldel - hold the delimeter that join individual items key and value

        arraydel = typeof arraydel == _UND ? "" : arraydel;
        keyvaldel = typeof keyvaldel == _UND ? "" : keyvaldel;

        //if obj is string
        if (_IsString(obj)) return obj;

        //if element
        if (_IsElement(obj)) return obj.toString();

        if (_IsObject(obj) || _IsArray(obj)) {
            var strf = "";
            var objkeys = Object.keys(obj);
            for (const key in objkeys) {
                //if (obj.hasOwnProperty(key)) {
                var element = obj[key];
                var str = keyvaldel != "" ? key + keyvaldel + element.toString() : element.toString();
                strf += str + arraydel;
                // }
            }
            return arraydel == "" ? strf : _TrimRight(strf, arraydel);
        }
        return obj.toString();
    }



    //Set the css style of an element
    //#Style #SetStyle
    _SetStyle = function (obj, styl) {
        //obj => the element(s) to set its style attribute (Array Allowed) -Required
        //NB: obj(String) - uses aim finder to get the element(s)
        //styl => the css rule to set -Required
        //NB: SetStyle updates rules or insert rules if not exist, does not delete existing rules

        obj = _IsString(obj) ? _(obj) : obj; //if obj is string use finder to get the element

        //if obj is array perform it on all elemnts of the array
        if (_IsArray(obj)) {
            var rtnarr = [];
            _Each(obj, function (iobj, styln) {
                var st = _SetStyle(iobj, styln);
                if (_IsArray(st)) {
                    rtnarr = rtnarr.concat(st);
                } else {
                    rtnarr.push(st);
                }

            }, styl);
            return rtnarr;
        }
        if (!_IsElement(obj)) return "";

        if (_Trim(styl) == "") return obj.style.cssText;
        //get current element rules in array format (associative array)
        var currRulearr = _ToStyleArray(obj.style.cssText);
        //convert new rul to array format (associative array)
        var newRulearr = _ToStyleArray(styl);
        var newRulearrkeys = Object.keys(newRulearr);
        //insert or update style rule in current style array, by loop through all element of new style array
        for (const atrn of newRulearrkeys) {
            currRulearr[atrn] = newRulearr[atrn];
        }
        //return the updated current style array to style string an set the object style
        obj.style.cssText = _ToStyleString(currRulearr);
        return obj.style.cssText;
    }

    //Covert CSS Rule String to array using ToArray method
    //Globalized (css.ToStyleArray())
    //#_.ToStyleArray
    _ToStyleArray = function (css) {
        //css the css rule string/Element style to convert to array - Required
        css = _IsElement(css) ? css.style.cssText : css;
        //alert(css); 
        if (!_IsString(css) || _Trim(css) == "") {
            return [];
        }
        //keyType is 1=Associate key only
        return _ToArray(css, ";", ":", 0);
    }


    //Covert Data String to array using ToArray method
    //Globalized (css.ToDataArray())
    //#_.ToDataArray
    _ToDataArray = function (data) {
        //data - the data string to convert to array - Required
        data = _IsElement(data) ? _Text(data) : data;
        //alert(css); 
        if (!_IsString(data) || _Trim(data) == "") {
            return [];
        }
        return _ToArray(data, "&", "=", 0);
    },


        //Convert Object, asscociate array to string
        _ToString = function (obj, arraydel, keyvaldel) {
            //obj - holds the array or accociate array  to convert
            //arraydel - hold the delimiter that joins each object items
            //keyvaldel - hold the delimeter that join individual items key and value

            arraydel = typeof arraydel == _UND ? "" : arraydel;
            keyvaldel = typeof keyvaldel == _UND ? "" : keyvaldel;

            //if obj is string
            if (_IsString(obj)) return obj;

            //if element
            if (_IsElement(obj)) return obj.toString();

            if (_IsObject(obj) || _IsArray(obj)) {
                var strf = "";
                var objkeys = Object.keys(obj);
                for (const key of objkeys) {
                    // if (obj.hasOwnProperty(key)) {
                    var element = obj[key];
                    var str = keyvaldel != "" ? key + keyvaldel + element.toString() : element.toString();
                    strf += str + arraydel;
                    // }
                }
                return arraydel == "" ? strf : _TrimRight(strf, arraydel);
            }
            return obj.toString();
        }

    //Convert Array or Object to Css String
    //Globalized (obj.ToStyleString())
    //#_.ToStyleString
    _ToStyleString = function (obj) {
        //obj => hold the object/item to convert to css string - Required
        return _ToString(obj, ";", ":");
    },

        //Convert Array or Object to Data String
        //Globalized (obj.ToDataString())
        //#_.ToDataString
        _ToDataString = function (obj) {
            //obj => hold the object/item to convert to css string - Required
            return _ToString(obj, "&", "=");
        },

        //Set the syle of an element
        //NB: note will not overwrite existing style will only update and insert if not exist
        //Globalized (elem.Style(sty))
        //#_Style
        _Style = function (elem, sty) {
            //elem => the element(s) to set its style attribute (Array Allowed) -Required
            //NB: if elem is String - uses aim finder to get the element(s)
            //sty => the css rule to set -Required
            //NB: Style method updates rules or insert rules if not exist, does not delete existing rules
            return _SetStyle(elem, sty);
        }



    //Globalized elemobj.Dimension()
    //#GetDimension #Dimension
    _GetDimension = function (elemobj) {
        //Get the elements dimension
        elemobj = _RP(elemobj);
        elemobj = _IsFound(elemobj) ? elemobj : {}
        //if multiple object dimension is to be gotten
        if (_IsArray(elemobj)) {

            var newarr = [];
            for (var d = 0; d < elemobj.length; d++) {
                var dim = _GetDimension(elemobj[d]);
                if (_IsArray(dim)) {
                    newarr = newarr.concat(dim);
                } else {
                    newarr.push(dim);
                }
            }
            return newarr;
        }

        var elemdim = _IsElement(elemobj) ? elemobj.getBoundingClientRect() : elemobj;
        if (typeof elemdim.width == _UND || typeof elemdim.height == _UND || (_ToInteger(elemdim.height) == 0 && _ToInteger(elemdim.width) == 0)) { //if getBoundingClientRect() not supported
            //_.Log(elemobj.offsetWidth);
            if (typeof elemdim.offsetWidth == _UND || typeof elemdim.offsetHeight == _UND || (_ToInteger(elemdim.offsetHeight) == 0 && _ToInteger(elemdim.offsetWidth) == 0)) {
                //if cannot get offset dimention
                //use calculated style
                var calstyle = window.getComputedStyle(elemobj);
                elemdim.width = _ToInteger(calstyle.width);
                elemdim.height = _ToInteger(calstyle.height);
                elemdim.top = _ToInteger(calstyle.top);
                elemdim.left = _ToInteger(calstyle.left);
                elemdim.right = _ToInteger(calstyle.right);
                elemdim.bottom = _ToInteger(calstyle.bottom);
            } else {//offset dimention is available
                elemdim.width = elemobj.offsetWidth;
                elemdim.height = elemobj.offsetHeight;
                elemdim.top = elemobj.offsetTop;
                elemdim.left = elemobj.offsetLeft;
                elemdim.right = elemobj.offsetRight;
                elemdim.bottom = elemobj.offsetBottom;
            }
        }
        return elemdim;
    }


    //object to deals with asigning and unasigning event handler to objects events
    //#Handlers #Assign #UnAssign
    _Handlers = {
        Mutations: {},
        Assigns: {},
        //assign an event handler to element(s)
        Assign: function (eventName, element, handler, once, keepevent) {
            //eventname => the name of the user event (on prefix not required) - Required
            //element => The DOM element to attach event handler to -Required
            //handler => the function to run when user triggers the event
            //once => determine if the event handler is to be run once i.e after running remove attahced event handler

            //set once default if not set
            once = typeof once == _UND ? false : once;
            keepevent = typeof keepevent == _UND ? true : keepevent;
            if (_IsArray(element)) { //if array, get individual item and perform asign

                for (var aad = 0; aad < element.length; aad++) {
                    elementc = element[aad];
                    _Handlers.Assign(eventName, elementc, handler, once);
                }
                return;

            } else if (_IsString(element)) { //if element is a string use the aim finder to get the element using the the string as selector before running the event asigning

                _Handlers.Assign(eventName, _(element), handler, once);
                return;
            }

            //if DONChange event
            if (eventName == "DOMChange") {

                if (typeof _Handlers.Mutations[element.ID] == _UND) {
                    // _Handlers.Mutations[element.ID].disconnect();
                    _Handlers.Mutations[element.ID] = new MutationObserver(function () {
                        handler({ "target": element, "currentTarget": element })
                    });
                    //alert('aa');
                    _Handlers.Mutations[element.ID].observe(element, {
                        attributes: false,
                        characterData: true,
                        childList: true,
                        subtree: true,
                        attributeOldValue: false,
                        characterDataOldValue: true
                    });
                }

                return;
            }

            //create handler function for once operation (if once package the handler with the UnAssign function in a function representing the event handler). the means that after the  running of the handler it will be removed
            var rhandler = once ? function (e) { handler(e); _Handlers.UnAssign(eventName, e.currentTarget, arguments.callee) } : handler;


            //if addEventListiner is supported by the browser
            if (element.addEventListener) {

                element.addEventListener(eventName, rhandler, false);
                // alert(rhandler)
            }
            //if attachEvent is supported
            else if (element.attachEvent) {
                element.attachEvent('on' + eventName, rhandler);
            }
            else {
                //add to the element DOM property
                element['on' + eventName] = rhandler;
            }
            //_.Log(eventName + " assign to " + element.ID);
            //add to global asign list

            if (keepevent) {
                if (typeof _Handlers.Assigns[element.ID] == _UND) _Handlers.Assigns[element.ID] = {};
                if (typeof _Handlers.Assigns[element.ID][eventName] == _UND) _Handlers.Assigns[element.ID][eventName] = [];
                //if(_Handlers.Assigns[element.ID][eventName].length < 5){
                //_.Log(element.ID + " : "+eventName + " : "+rhandler);
                _Handlers.Assigns[element.ID][eventName].push(rhandler);
                //}

            }
            return false;
            //obj.addEventListener(ev,func);
        },
        UnAssign: function (eventName, element, handler) {

            //eventname => the name of the user event (on prefix not required) - Required
            //element => The DOM element to remove/detach event handler -Required
            //handler => the handler to remove
            //NB: if no handler sent, remove all element handlers
            if (_IsArray(element)) { //if array, get individual item and perform asign
                for (var aad = 0; aad < element.length; aad++) {
                    elementc = element[aad];
                    _Handlers.UnAssign(eventName, elementc, handler);
                }
                return;
            } else if (_IsString(element)) { //if string use aim finder and perform unasign on seen element
                _Handlers.UnAssign(eventName, _(element), handler);
                return;
            }
            if (!_IsFound(element)) return;
            //if a valid handler function is sent

            if (_IsFunction(handler)) {
                //if removeEventListener is supported
                if (element.removeEventListener) {
                    element.removeEventListener(eventName, handler);
                }
                //if detachEvent is supported
                else if (element.detachEvent) {
                    element.detachEvent('on' + eventName, handler);
                }

                else {
                    element['on' + eventName] = function () { };
                }
            } else { //if no valid handler sent remove all
                //remove all assigned eventlistiners
                // element.ReplaceWith(element.Cloner());
                /* var cloneelement = element.cloneNode(true)
                 element.parentElement.replaceChild(cloneelement,element);*/
            }
        }
    }

    _Assign = _Handlers.Assign;
    _UnAssign = _Handlers.UnAssign;

    //This function create a copy of an element
    //#Cloner
    _Cloner = function (elems, withevent, deep) {
        withevent = withevent || false;
        deep = deep || true;
        //idpre = idpre || "";

        var EventAsigner = function (oldelem, newelem) {
            var eventassigns = _Handlers.Assigns;
            //get event assigned to elem using aim assign
            if (typeof eventassigns[oldelem.ID] != _.UND) {
                var elemfuncs = eventassigns[oldelem.ID];
                var objkeys = Object.keys(elemfuncs);
                for (const elemev of objkeys) {
                    //if (elemfuncs.hasOwnProperty(elemev)) {
                    var funarr = elemfuncs[elemev];
                    var clen = funarr.length + 0;
                    //alert(funarr.length); 
                    //assign all the function to the clone element
                    for (var cnt = 0; cnt < clen; cnt++) {
                        //_.Log(clen);
                        //_.Log(newelem.ID + " : "+elemev+ " : "+funarr[cnt]);
                        //newelem.addEventListener(elemev, funarr[cnt],false);
                        //_.Engine.Handlers.Assigns[element.ID][eventName].push(rhandler);

                        _Assign(elemev, newelem, funarr[cnt], false, false);
                    }
                    // }
                }
            }
            return true;
        }
        elem = _RP(elems);
        if (_IsFound(elem)) { //if element found
            if (_IsArray(elem)) { //if multiple element found
                //copy each and form array of clone elements
                var rtnarr = [];
                for (var ss = 0, to = elem.length; ss < to; ss++) {
                    var newclone = elem[ss].cloneNode(deep);
                    if (withevent) EventAsigner(elem[ss], newclone);
                    rtnarr.push(newclone);

                }
                return rtnarr;
            } else {
                var newclone = elem.cloneNode(deep);
                if (withevent) EventAsigner(elem, newclone);
                // if(withevent)_.Log("old:"+elem.ID+" new:"+newclone.ID);
                return newclone;
            }
        } else {
            return elems;
        }


    }

    _File = {
        Param: [],
        Local: function (param, fileelemcontainer) {
            //param => User defined parameter -Required
            //*FileID: the hide of the file input element to be created (must be unique)

            //* OnStart: function that will be executed on individual file selected before reading.
            //OnStart(param) - param =>all user set parameters + param._File,param.Index,param.Length

            //* OnLoad: function to run (individually) on selected file when read complete 
            //OnLoad(param) - param =>all user set parameters + param.File,param.Index,param.Length,param.Reader,param.Result

            //* OnError: function to run when error occur on individual file selected
            //OnError(param) - param =>all user set parameters + param.File,param.Index,param.Length,param.Error

            //* MinSize: the minimum file size allowed (in Byte)

            //* MaxSize: the maximum file size allowed (in Byte)

            //* Accept: the file type or extention allowed (File Element Accept attribute)

            //* Multiple: if multiple selection is allowed or not (true/false) 

            //* ReadType: the format file will be read, (Text-Default, DataURL, ArrayBuffer, BinaryString, FileName)

            //Create a virtual element so as to generate unique id using FileID()
            // var obj = {id:"",tagName:"div"};
            objid = typeof param['FileID'] == "undefined" || _Trim(param['FileID']) == "" ? _GetId({ id: "", tagName: "div" }) : param['FileID'];
            var fileid = objid; //the id of the file element to be created
            //alert(fileid);
            param = typeof param == "undefined" ? {} : param;
            //Set Default values as required
            param.Accept = typeof param.Accept == "undefined" ? "" : param.Accept;
            param.Multiple = typeof param.Multiple == "undefined" ? false : param.Multiple;
            param.MinSize = typeof param.MinSize == "undefined" ? 0 : param.MinSize;
            param.MaxSize = typeof param.MaxSize == "undefined" ? 0 : param.MaxSize;
            _File.Param[fileid] = param; //set the user difined parameter globally
            //form the multiple html file attribute
            var multsel = param.Multiple ? "multiple" : ""
            //check if no file element exist with the current file id, create a new one
            var createdfile = _(fileid);
            var found = false;
            if (_IsFound(createdfile)) {
                //check if it is file element
                if (typeof createdfile.type != "undefined" && createdfile.type == "file") {
                    //leave it
                    found = true;
                } else {
                    //remove it
                    createdfile.parentElement.removeChild(createdfile);
                }
            }
            if (!found) { //if not already exist
                //form the html code for the file element
                var htmls = '<input type="file" id="' + fileid + '" name="' + fileid + '" onchange="_File.Process(this);" style="position:absolute;display:none;visibility:hidden" accept="' + param.Accept + '" ' + multsel + ' />';
                var filecontainer = (typeof fileelemcontainer == "undefined" || !_IsElement(fileelemcontainer)) ? document.body : fileelemcontainer;
                //alert(filecontainer.outerHTML)
                //append it to the body
                filecontainer.insertAdjacentHTML('beforeend', htmls);
            }

            //Click the new added file element, to start
            _(fileid).click();
            //return the newly created file element id
            return fileid;

            //}

        },
        //When onchange event handler for the aim file upload object _File.Local
        Process: function (file) {
            //file => the file element
            //if file(s) are selected
            if (file.files.length > 0) {

                //function to calculate size in KB
                var FileSize = function (size) {
                    var sizestr = "";
                    if (size > 1023) {
                        size = Math.floor(size / 1024);
                        sizestr = size + "KB";
                    } else {
                        sizestr = size + "B";
                    }
                    return sizestr;
                }

                var param = _File.Param[file.id];//get user defined parameters
                //loop through individual file selected
                for (var ind = 0; ind < file.files.length; ind++) {
                    var err = ""; //Holds the error info
                    var rfile = file.files[ind]; //the current file
                    var cind = ind; //the current file index
                    var fsize = ('size' in rfile) ? rfile.size : rfile.fileSize;

                    if (fsize < param.MinSize) { //if file size is below min size
                        var sizestr = FileSize(param.MinSize);
                        err = "File too small; Minimum Upload Size (" + sizestr + ")";
                    } else if (fsize > param.MaxSize && param.MaxSize > 0) {
                        //file size above max size
                        var sizestr = FileSize(param.MaxSize);
                        err = "File exceeded Maximum Upload Size (" + sizestr + ")";
                    }
                    //run the onstart handler, sending the file element and the current selected file index
                    if (typeof _File.Param[file.id].OnStart != "undefined") {
                        param.File = file;
                        param.Index = ind;
                        param.Length = file.files.length

                        _File.Param[file.id].OnStart(param)

                    }


                    //if error occur
                    if (err != "") {
                        //if error function or object passed
                        if (typeof param.OnError != "undefined") {
                            if (_IsFunction(param.OnError)) {
                                param.Error = err;
                                param.File = file;
                                param.Index = ind;
                                param.Length = file.files.length
                                param.OnError(param);
                            } else { //if an element / selector string / array
                                _Text(param.OnError, err);
                            }
                        } else {
                            alert(err);
                        }

                        continue;
                    }



                    //alert(rfile.type)
                    //if filename needed, get the current filename return param to user defined OnLoad method and ignore other operation, continue to the next file if exist and check if reader is not supported
                    if ((typeof _File.Param[file.id].ReadType != "undefined" && _File.Param[file.id].ReadType == "FileName") || typeof _File.Param[file.id].ReadType == "undefined" || (!window.FileReader || !window.Blob) || _Trim(rfile.type) == "") {
                        param.Result = ('name' in rfile) ? rfile.name : rfile.fileName;
                        param.File = file;
                        param.Index = ind;
                        param.Length = file.files.length;
                        param.Reader = {};
                        _File.Param[file.id].OnLoad(param);
                        continue;
                    }


                    //create the reader object
                    var reader = new FileReader();
                    //keep file index in reader (#=>Need Better way)
                    reader.id = ind;

                    //the onload event handler of the reader
                    reader.onload = function () {
                        //check if user defined handler exist
                        //run it, sending the reader result, file element, the file index
                        if (typeof _File.Param[file.id].OnLoad != "undefined") {
                            // var param = _File.Param[file.id];
                            param.Result = this.result;
                            param.File = file;
                            param.Index = this.id;

                            param.Length = file.files.length;
                            param.Reader = this;
                            _File.Param[file.id].OnLoad(param)
                        }
                    }
                    //the onerror event handler
                    reader.onerror = function () {
                        //check if user defined handler exist
                        //run it, sending the reader error, file element, the file index
                        if (typeof _File.Param[file.id].OnError != "undefined") {
                            param.Error = this.error;
                            param.File = file;
                            param.Index = this.id;
                            param.length = file.files.length
                            _File.Param[file.id].OnError(param)
                        }
                    }

                    //check the user defined readType and read file based on it
                    //NB: default is readAsText ArrayBuffer BinaryString
                    // if(typeof _File.Param[file.id].ReadType != "undefined"){
                    var rtype = _File.Param[file.id].ReadType;
                    if (_Trim(rtype.toLowerCase()) == 'dataurl') {
                        reader.readAsDataURL(rfile);
                    } else if (_Trim(rtype.toLowerCase()) == 'arraybuffer') {
                        reader.readAsArrayBuffer(rfile);
                    } else if (_Trim(rtype.toLowerCase()) == 'binarystring') {
                        reader.readAsBinaryString(rfile);
                        /*}else if(rtype == 'Text'){
                          reader.readAsText(rfile); */
                    } else {
                        reader.readAsText(rfile);
                    }
                    //}else{
                    // reader.readAsText(rfile);
                    //}


                    // }
                }
            }


            // var obj = _(file.id.split("_")[0]);


        }
    }

    //Set the obj Attribute from a local file using Aim Browse method
    //Globallized - obj.Browse(params)
    //#_.BrowseSetFile #BrowseSetFile
    _BrowseSetFile = function (obj, objparam) {

        //obj => the object to load with data from the client system

        //objparam => User defined parameters

        //1. SetAttribute => the obj attribute to set with the read data.
        //Default: image => src, others => aim.Text()
        //2. ReadType: the format file will be read as (Aim Browse ReadTypes)
        //Default: image => DataURL, others => FileName
        //3. Accept: the type of file to filter out in dialog display (File element Accept Attribute)
        //Default: image => "image/*", all file
        //4. MaxSize => the maximum allowed file size
        //Default: any size
        //5. MinSize => the minimum allowed file size
        //Default: any size
        //6. Multiple => determine if multiple item selection is allowed (true/false)
        //Default: if obj is more than 1(array of obj)=> true, if one obj to set=> false
        //NB: if obj is more than one and client item selected is more than one, will automatically set individual client selection to individual obj
        //7. OnLoad => the method to call when obj is set successfully
        //OnLoad(Aim Browse OnLoad Param + Param.Element-the element to set)
        //NB: if image: onload is called when the image set has completely loaded in the image element
        //8. OnStart => the method to call when the reading/setting operation starts 
        //OnStart(Aim Browse OnStart Param + Param.Element-the element to set)
        //9. FileID => the id of the newly created file input element

        obj = _RP(obj);
        objparam = typeof objparam == "undefined" ? {} : objparam;
        //get the first file selected
        var fobj = _IsArray(obj) ? obj[0] : obj;
        //the object attribute to set
        //  var objattr = typeof objparam.SetAttribute == "undefined"?"":objparam.SetAttribute;
        var objattr = "";
        //the read DataType to use
        var objreadDataT = typeof objparam.ReadType == "undefined" ? "" : objparam.ReadType;

        //the accept file type
        if (typeof objparam.Accept == "undefined") {
            objparam.Accept = _IsElement(fobj) && fobj.tagName.toLowerCase() == 'img' ? "image/*" : "";
        }
        //console.log(objattr);
        //
        //var readType = 
        //if multiple image to set
        if (_IsFound(obj)) {
            // alert(obj);
            return _File.Local({
                FObject: obj, //the object to load data into (Array Allowed)
                FileID: typeof objparam.FileID == "undefined" ? _GetId(obj) + "_File" : objparam.FileID,
                //Maximum File size allowed (default - any size)
                MaxSize: typeof objparam.MaxSize == "undefined" ? 0 : objparam.MaxSize,
                //Minimum File size allowed (default - 0)
                MinSize: typeof objparam.MinSize == "undefined" ? 0 : objparam.MinSize,
                OReadType: objreadDataT,
                //Multiple Selection (default - false)
                Multiple: typeof objparam.Multiple == "undefined" ? _IsArray(obj) ? true : false : objparam.Multiple,
                //OnFinish is use to send user defined Onload to method
                OnFinish: typeof objparam.OnLoad == "undefined" ? function () { } : objparam.OnLoad,
                //OnBigin use to send user define OnStart to method
                OnBigin: typeof objparam.OnStart == "undefined" ? function () { } : objparam.OnStart,
                //Type of file type to filter or display by the browse dialog (default - all)
                Accept: objparam.Accept,
                //The Element Attribute to set
                ObjectAttr: objattr,
                //OnFailed use to send user defined OnError Method
                OnFailed: typeof objparam.OnError == "undefined" ? function () { } : objparam.OnError,
                //OnStart Aim Browse OnStart Method (will run OnBigin in it)
                OnStart: function (param) { //the operation/action to perform before reading the file from client system
                    //get the current object to set

                    var curobj = _IsArray(param.FObject) ? param.FObject[param.Index] : param.FObject;
                    var curobjID = _GetId(curobj);


                    //if the current object is an image or object element, set the read type to DataURL
                    if (_IsElement(curobj) && (curobj.tagName.toLowerCase() == 'img' || curobj.tagName.toLowerCase() == 'object')) {
                        //force the ReadType to be DataUrl if not set
                        _File.Param[param.File.id].ReadType = _Trim(param.OReadType) == "" ? "DataURL" : param.OReadType;

                    } else {
                        //force the ReadType to be FileName if not set
                        _File.Param[param.File.id].ReadType = _Trim(param.OReadType) == "" ? "FileName" : param.OReadType;
                    }
                    //Globally keep the index of the current file in a global array
                    //Workaround for getting the loaded file index inside its onload handler if image or object
                    if (typeof GFilesParm[curobjID] == "undefined") {
                        //set the current file index globally using the current element id as the key
                        GFilesParm[curobjID] = param.Index;
                    }
                    //set the current params global as well
                    _File.Param[curobjID] = param;

                    // run the user defined OnLoad Method( sent as OnBigin) sending the following parameters as an object Element,Index,File and Type
                    param.OnBigin({ Element: curobj, Index: param.Index, File: param.File, Type: param.File.files[param.Index].type });//User define function (OnStart)


                },
                //Aim Browse OnLoad Handler
                OnLoad: function (paramu) {

                    var curobj = _IsArray(paramu.FObject) ? paramu.FObject[paramu.Index] : paramu.FObject;

                    //get the current elements tagname
                    var tgn = curobj.tagName.toLowerCase();
                    //if the current object ia an image, set the src attribute
                    if (_IsElement(curobj) && (tgn == 'img' || tgn == 'object')) {

                        var tgNAttr = { 'img': 'src', 'object': 'data' }
                        var nattr = _Trim(paramu.ObjectAttr) == "" ? tgNAttr[tgn] : paramu.ObjectAttr
                        //console.log(nattr);
                        if (nattr == "data" || nattr == "src") {
                            var dofinish = true;
                            //keep the current image id and the parameter details in the global param array object for use in the image onload event handler
                            //var curobjID = _.ID(curobj);
                            // File.Param[curobjID] = param;
                            //alert(typeof curobj.onload)
                            if (typeof curobj.onload != "undefined") { //if the current element curobj supports onload event handlers
                                curobj.onload = function () {
                                    var obj = {};
                                    obj.Element = this;
                                    obj.Index = GFilesParm[this.id];
                                    obj.File = _File.Param[this.id].File;
                                    obj.Type = obj.File.files[obj.Index].type

                                    _File.Param[this.id].OnFinish(obj);
                                }
                                dofinish = false;
                            }

                            //_('ee').innerHTML += '<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><p style="color:green">'+paramu.Result+'</p>';
                            curobj[nattr] = paramu.Result;
                            if (dofinish) {
                                paramu.OnFinish({ Element: curobj, Index: paramu.Index, File: paramu.File, Type: paramu.File.files[paramu.Index].type })
                            }

                        } else {
                            curobj[paramu.ObjectAttr] = paramu.Result;
                            //param.Element = curobj;
                            paramu.OnFinish({ Element: curobj, Index: paramu.Index, File: paramu.File, Type: paramu.File.files[paramu.Index].type });
                        }

                    } else { //others set thier _Text (value or textContent)

                        if (_Trim(paramu.ObjectAttr) == "") { //if no attribute
                            _Text(curobj, paramu.Result); //set the Text
                        } else { //if attribute exist, set the attribute
                            curobj[paramu.ObjectAttr] = paramu.Result;
                        }
                        paramu.Element = curobj;
                        paramu.OnFinish({ Element: curobj, Index: paramu.Index, File: paramu.File, Type: paramu.File.files[paramu.Index].type });
                    }

                },
                //Aim Browse OnError Method (will run the User define OnError Method(OnFailed) if set)
                OnError: function (params) {
                    var curobj = _IsArray(params.FObject) ? params.FObject[params.Index] : params.FObject; //get the current obj
                    //get the current elements tagname
                    var tgn = curobj.tagName.toLowerCase();
                    //Get the current file type
                    var type = params.File.files[params.Index].type;
                    //if user defined OnError is set (sent as OnFailed) run it sending an object of Element,File,Index,Type and Error
                    if (typeof params.OnFailed != "undefined" && params.OnFailed != null) {
                        params.OnFailed({ Element: curobj, File: params.File, Index: params.Index, Error: params.Error, Type: type });
                        return
                    }

                    //if no user defined Error handler sent

                    if (tgn == "img") { //if type image
                        curobj.alt = params.Error;
                    } else if (tgn == "object") { //if type object
                        curobj.alt = params.Error;
                        curobj.standBy = params.Error;
                    } else { //others
                        _Text(curobj, params.Error);

                    }



                }

            }, obj.parentElement);
        }
        return null;
    }

    //Browse file from client local system and load it with the appropriate html element to display it in the main container
    //for individual file, template can be set (uses aim Template Object)
    //Globalize mcont.BrowseInto(params)
    //#_.BrowseInto
    _BrowseInto = function (mcont, param) {
        //mcont: the html element to load with user selected/browsed files
        //NB: if container(mcont) object is array the first is used
        //param: is same as _.Browse Method + param.TemplateID
        //NB: the OnLoad,OnStart,OnError handler recieve object with the following attributes
        //1. Index=> the curent file index of the files selected loaded into the mcont
        //2. File=> the file object (Element) dynamically created by aim
        //3. Element=> the element representin the file loaded (if image:img(src), text:p(innerHTML), others:object(data))
        //4. Type => the type of the current file loaded (file element file type)
        //5. Error (for OnError only) => the eroor string

        //set defaults
        mcont = typeof mcont == "undefined" || mcont == null ? document.body : mcont;
        param = typeof param == "undefined" ? {} : param;
        param.TemplateID = typeof param.TemplateID == "undefined" ? "" : param.TemplateID;

        //Use Aim Browse Method
        _File.Local({
            //ID of the newly created file input element
            FileID: typeof param.FileID == "undefined" ? "" : param.FileID,
            //The Container to load the files into
            Container: _RP(mcont),
            //Multiple Selection (default - false)
            Multiple: typeof param.Multiple == "undefined" ? false : param.Multiple,
            //Type of file type to filter or display by the browse dialog (default - all)
            Accept: typeof param.Accept == "undefined" ? "" : param.Accept,
            //Maximum File size allowed (default - any size)
            MaxSize: typeof param.MaxSize == "undefined" ? 0 : param.MaxSize,
            //Minimum File size allowed (default - 0)
            MinSize: typeof param.MinSize == "undefined" ? 0 : param.MinSize,
            //Individual file html template id
            TemplateID: param.TemplateID,
            //OnFinish is use to send user defined Onload to method
            OnFinish: typeof param.OnLoad == "undefined" ? function () { } : param.OnLoad,
            //OnBigin use to send user define OnStart to method
            OnBigin: typeof param.OnStart == "undefined" ? function () { } : param.OnStart,
            //OnFailed use to send user defined OnError Method
            OnFailed: typeof param.OnError == "undefined" ? null : param.OnError,
            //OnStart Aim Browse OnStart Method (will run OnBigin in it)
            OnStart: function (params) {
                //get the current file type
                var type = params.File.files[params.Index].type;
                //always use the first container element if multiple container is sent
                params.Container = _IsArray(params.Container) ? params.Container[0] : params.Container;
                //if it is the first (clear the container)
                if (params.Index == 0) {
                    params.Container.innerHTML = "";
                }
                //if the place holder not exist
                if (!_IsFound(_(params.File.id + params.Index))) {
                    if (_Trim(type) != "") {//if can identify type
                        //break the type string
                        var types = type.split("/")[0].toLowerCase();
                        if (types == "text") { //if type is a text file
                            //Change the Aim Browse ReadType to ReadAsText
                            _File.Param[params.File.id].ReadType = "Text";
                            //Set the html elemnt (placeholder) that will display the file (Text - p element)
                            var wraper = '<p id="' + params.File.id + params.Index + '"></p>';
                        } else if (types == "image") {//if type is image
                            //Set the html elemnt (placeholder) that will display the file (Image - img element)
                            var wraper = '<img id="' + params.File.id + params.Index + '" src="" alt="Loading.."/>';
                            //Change the Aim Browse ReadType to ReadAsDataURL
                            _File.Param[params.File.id].ReadType = "DataURL";
                        } else {//if type is any other element (try load it using html object element(placeholder))
                            var wraper = '<object id="' + params.File.id + params.Index + '" data="" alt="Loading.." standby="Loading.." />';
                            //Change the Aim Browse ReadType to ReadAsDataURL
                            _File.Param[params.File.id].ReadType = "DataURL";
                        }
                    } else {
                        //if cannot identify type
                        //Read/Set as Text 
                        _File.Param[params.File.id].ReadType = "Text";
                        var wraper = '<p id="' + params.File.id + params.Index + '"></p>';
                    }
                }

                //Globally keep the index of the current file in a global array
                //Workaround for getting the loaded file index inside its onload handler if image or object
                if (typeof GFilesParm[params.File.id + params.Index] == "undefined") {
                    //set the current file index globally using the combination of the file element id and the curent index (which is also the placeholder html element id) as the key
                    GFilesParm[params.File.id + params.Index] = params.Index;
                }
                //set the current params global as well
                _File.Param[params.File.id + params.Index] = params;

                if (params.TemplateID == "") {
                    //params.TemplateID = param.FileID;
                    //Template.Add(params.TemplateID,'<div style="display:inline-block"><button type="button" onclick="this.parentElement.parentElement.removeChild(this.parentElement);"><i class="fas fa-times"></i></button>{{'+params.TemplateID+'}}</div>');
                }
                //If template ID is sent
                if (params.TemplateID != "") {

                    //set the current html placeholder in an object (using the template id as key) - to be used to file the user created template
                    var objpar = {};
                    objpar[params.TemplateID] = wraper;
                    //Fill the template and include the resultance html to the container element
                    params.Container.insertAdjacentHTML('beforeend', Template.Fill(params.TemplateID, objpar));

                } else {
                    //if no template to be use, just append the place holder to the container elements
                    params.Container.insertAdjacentHTML('beforeend', wraper);
                }
                //if user defined OnStart is set (sent in as OnBigin) run it sending the Element,Index,Type and File as an object
                params.OnBigin({ Element: _(params.File.id + params.Index), Index: params.Index, File: params.File, Type: params.File.files[params.Index].type });
            },
            //Aim Browse OnLoad Method (will run the User define OnLoad Method(OnFinsih) if set)
            OnLoad: function (params) {
                //Get the current file type
                var type = params.File.files[params.Index].type;
                var curobj = _(params.File.id + params.Index); //get the current file placeholder
                //if can identify the type
                if (_Trim(type) != "") {
                    var types = type.split("/")[0].toLowerCase();
                    if (types == "text") { //if a text file
                        //set the innerHTML of the placeholder(p element) by it id (which was set as the combination of the file id and the current file index) to the reader result (read as Text)
                        curobj.innerHTML = params.Result;
                        //if user defined OnLoad is set (sent in as OnFinish) run it sending the Element,Index and File as an object
                        params.OnFinish({ Element: curobj, Index: params.Index, File: params.File });
                    } else if (types == "image") { //if file is an image
                        //Set the onload handler for the image
                        //forcing the User defined OnLoad(sent as OnFinish) method to run when image loads completely
                        curobj.onload = function () {
                            var obj = {};
                            obj.Element = this;
                            obj.Index = GFilesParm[this.id];
                            obj.File = _File.Param[this.id].File;
                            obj.Type = obj.File.files[obj.Index].type
                            //if user defined OnLoad is set (sent in as OnFinish) run it sending the Element,Index and File as an object
                            _File.Param[this.id].OnFinish(obj)
                        }

                        //set the src of the placeholder(img element)(which was set as the combination of the file id and the current file index) to the reader result (read as DataURL)
                        curobj.src = params.Result;
                    } else {
                        //if type of file selected is not text and image, i.e an object element is created to load the file. Hense, set the data attribute
                        var dofinish = false;
                        //if the html object onload handler is supported
                        if (typeof curobj.onload != "undefined") {
                            //set the onload andler, same as that of image type
                            curobj.onload = function () {
                                var obj = {};
                                obj.Element = this;
                                obj.Index = GFilesParm[this.id];
                                obj.File = File.Param[this.id].File;
                                obj.Type = obj.File.files[obj.Index].type
                                _File.Param[this.id].OnFinish(obj);
                            }
                            dofinish = false;
                        }


                        //set the curent html object element data attribute to the reader result (read as DataURL)
                        curobj.data = params.Result;
                        if (dofinish) {
                            //if curobj.onload not supported just run the OnFinish sending required parameter
                            params.OnFinish({ Element: curobj, Index: params.Index, File: params.File, Type: params.File.files[params.Index].type });
                        }


                    }
                } else {
                    //if cannot identify type, just insert the plain text as innerhtml of the created p element and run the user defined OnLoad method (sent as OnFinish)
                    curobj.innerHTML = params.Result;
                    params.OnFinish({ Element: curobj, Index: params.Index, File: params.File, Type: params.File.files[params.Index].type });
                }
                return;
            },
            //Aim Browse OnError Method (will run the User define OnError Method(OnFailed) if set)
            OnError: function (params) {
                var curobj = _(params.File.id + params.Index); //get the current file placeholder
                if (_IsFound(curobj)) {
                    //Get the current file type
                    var type = params.File.files[params.Index].type;
                    //if user defined OnError is set (sent as OnFailed) run it sending an object of Element,File,Index and Error
                    if (typeof params.OnFailed != "undefined" && params.OnFailed != null) {
                        params.OnFailed({ Element: curobj, File: params.File, Index: params.Index, Error: params.Error, Type: params.File.files[params.Index].type });
                        return
                    }

                    //if no user defined Error handler sent
                    if (_Trim(type) != "") { //if can identify type
                        type = type.split('/')[0];
                        if (type.toLowerCase() == "text") { //if type text
                            curobj.innerHTML = params.Error;
                        } else if (type.toLowerCase() == "image") { //if type image
                            curobj.alt = params.Error;
                        } else { //if type is object
                            curobj.alt = params.Error;
                            curobj.standBy = params.Error;
                        }
                    } else { //if cannot identify type
                        curobj.innerHTML = params.Error;
                    }

                }

            }
        }, _RP(mcont).parentElement);
    }

    //The Animation Engine Logic
    //************************************************************** */

    //Animation Globals
    //____________________________________________
    //Global array holding all animation
    _Animations = [],
        //pre-defined handlers
        _AnimationEffectJS = {
            Bounce: function (progress) {
                for (var a = 0, b = 1, result; 1; a += b, b /= 2) {
                    if (progress >= (7 - 4 * a) / 11) {
                        return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2);

                    }
                }
            }
        };
    //Builtin animation effect for css
    _AnimationEffects = {
        FadeIn: { 0: "opacity:0", 1: "opacity:1" },
        FadeOut: { 0: "opacity:1", 1: "opacity:0" },
        ZoomIn: { 0: "transform:scale(0);opacity:0", 1: "transform:scale(1);opacity:1" },
        ZoomOut: { 0: "transform:scale(1);opacity:1", 1: "transform:scale(0);opacity:0" },
        LeftIn: { 0: "left:-300px;opacity:0", 1: "left:0px;opacity:1" },
        TopIn: { 0: "top:-300px;opacity:0", 1: "top:0px;opacity:1" },
        RightIn: { 0: "right:-300px;opacity:0", 1: "right:0px;opacity:1" },
        BottomIn: { 0: "bottom:-300px;opacity:0", 1: "bottom:0px;opacity:1" },
        LeftOut: { 0: "left:0px;opacity:1", 1: "left:-300px;opacity:0" },
        TopOut: { 0: "top:0px;opacity:1", 1: "top:-300px;opacity:0" },
        RightOut: { 0: "right:0px;opacity:1", 1: "right:-300px;opacity:0" },
        BottomOut: { 0: "bottom:0px;opacity:1", 1: "bottom:-300px;opacity:0" },
        BounceIn: {
            0: "transform:translateY(-100%);opacity:0",
            0.05: "transform:translateY(-100%);",
            0.15: "transform:translateY(0);",
            0.30: "transform:translateY(-50%);",
            0.40: "transform:translateY(0%);",
            0.50: "transform:translateY(-30%);",
            0.70: "transform:translateY(0%);",
            0.80: "transform:translateY(-15%)",
            0.90: "transform:translateY(0%);",
            0.95: "transform:translateY(-7%);",
            0.97: "transform:translateY(0%);",
            0.99: "transform:translateY(-3%);",
            1: "transform:translateY(0);opacity:1"
        },
        BounceOut: {
            1: "transform:translateY(-100%);opacity:0",
            0.99: "transform:translateY(-100%);",
            0.97: "transform:translateY(0);",
            0.95: "transform:translateY(-50%);",
            0.90: "transform:translateY(0%);",
            0.80: "transform:translateY(-30%);",
            0.70: "transform:translateY(0%);",
            0.50: "transform:translateY(-15%)",
            0.40: "transform:translateY(0%);",
            0.30: "transform:translateY(-7%);",
            0.15: "transform:translateY(0%);",
            0.05: "transform:translateY(-3%);",
            0: "transform:translateY(0);opacity:1"
        }
    };
    //Easing
    _Easing = {
        // no easing, no acceleration
        'linear': function (t) { return t },
        // accelerating from zero velocity
        'ease-in': function (t) { return t * t },
        // decelerating to zero velocity
        'ease-out': function (t) { return t * (2 - t) },
        // acceleration until halfway, then deceleration

        'ease': function (t) { return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t },
        // accelerating from zero velocity 
        easeInCubic: function (t) { return t * t * t },
        // decelerating to zero velocity 
        easeOutCubic: function (t) { return (--t) * t * t + 1 },
        // acceleration until halfway, then deceleration 
        "ease-in-out": function (t) { return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1 },
        // accelerating from zero velocity 
        easeInQuart: function (t) { return t * t * t * t },
        // decelerating to zero velocity 
        easeOutQuart: function (t) { return 1 - (--t) * t * t * t },
        // acceleration until halfway, then deceleration
        easeInOutQuart: function (t) { return t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t },
        // accelerating from zero velocity
        easeInQuint: function (t) { return t * t * t * t * t },
        // decelerating to zero velocity
        easeOutQuint: function (t) { return 1 + (--t) * t * t * t * t },
        // acceleration until halfway, then deceleration 
        easeInOutQuint: function (t) { return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t }
    };
    //function to calculate delta base on the effect
    _CalculateDelta = function (progress, effect, ease, inverse) {
        delta = 0;
        //effect = _Trim(effect);
        progress = (inverse) ? 1 - progress : progress;
        /* if(effect.toLowerCase().substr(0,11) == "exponential" ){ //if style is exponential i.e slow from start and increase
     
            var poww = (effect.length == 11)?2:_ToInteger(effect.substr(11));
            poww = poww <= 2?2:poww;
             delta = Math.pow(progress,poww);
         }else if(effect.toLowerCase() == "elastic"){ 
            var x = 80; //the elastic index modify as required
             delta = Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*x/3*progress);
         }else if(effect.toLowerCase() == "bounce"){ //if style is bounce //code is complicated DUY(Dont Understand Yet)
             //progress = 1 - progress;
             for(var a = 0, b = 1, result; 1; a += b, b /= 2) {	    
               if (progress >= (7 - 4 * a) / 11) {	     
                delta = -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2);	
                break;
               }
             }
             
         }else{//no effect
             delta = progress;
         }*/
        return typeof _Easing[ease] == _UND ? _Easing['easeOutQuad'](delta) : _Easing[ease](delta);
    }
    //hold the Animation Directions an their css animation equivalent
    _CSSDirections = { "to": "normal", "fro": "reverse", "to-fro": "alternate", "fro-to": "alternate-reverse" };

    //Animation Globals Ends
    //_____________________________________________________________________


    //#Animation Object
    //___________________________________________________________
    _Animation = function (Param) {
        Param = Param || {};

        //the element(s) to animate (aim finder selector allowed, array allowed)
        this.Target = _RP(Param.Target || document.documentElement);

        //make target an array, to avoid checking if array or not every time is to be work on
        this.Target = !_IsArray(this.Target) ? [this.Target] : this.Target;

        //the delay time before animation start
        this.Delay = Param.Delay || 0;
        //functions / operation to trigger/run during animation
        //Structure : {Delta:<the animation time/level to run trigger>,Action:<the operation to perform>}
        //NB: Action can be an animation name , a function or a Goto keyframe number
        this.Trigger = Param.Trigger || {};
        //the trigger object that will be use technically, it will be edited at the cause of the animation, but when animation is restarted, it will be reset
        this.ProcessTrigger = {};

        //Set the animation name and add the animation to the aim animations global array
        if (typeof Param.Name != _UND && _IsString(Param.Name) && Param.Name != "") {
            //set the animation name
            this.Name = Param.Name;

        } else {
            var randname = _Rand.Number(10);
            while (typeof _Animations[randname] != _UND) {
                randname = _Rand.Number(10);
            }
            this.Name = randname;
        }


        //Type of Animation
        this.Type = Param.Type || "JS"; //JS | CSS
        //NB: CSS Animation Type do not support Pause operation

        /*//set the animation handler
        this.OnPlay = Param.OnPlay || _AnimationHandlers.FadeIn;
        //NB: OnPlay for CSS type should be the css rule to animate*/

        //animation duration
        this.Duration = Param.Duration || 1;

        //Start Time
        this.StartTime = null;

        //Effect
        this.Effect = Param.Effect || 'FadeIn'; //if String (pre defined effect), Function (user defined javascript effect - javascript animation), Object (user defined keyframes - css animation e.g {0:"cssRule1",1:"cssRule2"})

        this.Ease = Param.Ease || 'ease';



        //Infinite animation
        this.Infinite = Param.Infinite || false;

        //Retain animation last frame
        this.Retain = Param.Retain || true //default - true


        //animation State
        this.State = "Stop"; //Play | Pause | End | Stop (default)

        //animation Progress
        this.Progress = 0;

        //pause progress level
        this.ProgressStart = 0;

        //function to run when animation starts
        this.OnStart = Param.OnStart || function () { }

        //function to run when animation ends
        this.OnEnd = Param.OnEnd || function () { }

        //function to run when animation is paused
        this.OnPause = Param.OnPause || function () { }

        this.FromFrame = 0; //the frame index where css animation will start from, usually set by Goto method

        //internal method holding the direction
        this.Flow = "to";

        //the delay time (seconds)
        this.Delay = Param.Delay || 0;

        this.DirectionArray = ['to'];

        var AnimationObj = this; //make the animation object available to sub functions

        Object.defineProperties(AnimationObj, {
            'Type': { get: function () { return _IsFunction(this.Effect) ? "JS" : "CSS" } }
        });
        Object.defineProperties(AnimationObj, {
            'Direction': {
                get: function () { return this.Flow },
                set: function (dir) { this.Flow = dir; this.DirectionArray = dir.split("-") }
            }
        });

        //Animation Direction 
        AnimationObj.Direction = Param.Direction || "to"; //to | fro | to-fro | fro-to

        //The animation frame object
        this.AnimationFrame = null;


        //#CSS Animation Methods and Properties
        //**************************************************************    
        //#CSS Animation #Play Methods
        this.PlayCSSAnimation = function (fromFrame) {
            //#log
            // console.log("CSS Animation Module Entered");
            var dur_sec = (1 - this.FromFrame) * this.Duration;
            //#log
            // console.log("Calculated Duration: "+dur_sec*1000);
            //#log
            //console.log("Animation Delay: "+this.StartTime);
            //the number of times animation will move
            var animMovenum = this.DirectionArray.length;
            var animcnt = this.Infinite ? "infinite" : animMovenum;
            var mainduration = (dur_sec * 1000 * animMovenum) + (this.Delay * 1000);

            //if currently paused
            if (this.State == "Pause") {
                this.State = "Play";
                // console.log('Played after Pause');
                _Each(this.Target, function (tar) {
                    tar.style.animationPlayState = "running";
                });
                //this.Target.style.animationPlayState = "running";
                if (!this.Infinite) { //if not infinite animation, set the end detreminant timmer
                    this.AnimationFrame = setTimeout(this.CSSAnimationEnd, mainduration - this.ProgressStart);
                }

                //recalculate all avialable triggers time and #start timeout #Trigger
                this.StartCSSTrigger();

                this.StartTime = new Date().getTime();
                return;
            }
            this.State = "Play";

            //loop trough Effect keframes and form the css keyframe rule string
            if (_IsObject(this.Effect)) {
                var keyframes = "";
                var frame = "";
                //reset the FromFrame rule set during play goto operation
                var cnt = 0;
                var objkeys = Object.keys(this.Effect);
                for (const frame of objkeys) {
                    if (frame >= this.FromFrame) {
                        style = this.Effect[frame];
                        if (cnt == 0) { //initialize the last and first
                            this.LastFrameIndex = this.FirstFrameIndex = _ToFloat(frame);
                        }
                        //convert the frame progress to percentage
                        var progr = Math.floor(_ToFloat(frame) * 100);
                        //add to keyframes
                        keyframes += progr + "%{" + style + "}";
                        this.FirstFrameIndex = _ToFloat(frame) < this.FirstFrameIndex ? _ToFloat(frame) : this.FirstFrameIndex;
                        this.LastFrameIndex = _ToFloat(frame) > this.LastFrameIndex ? _ToFloat(frame) : this.LastFrameIndex;
                        cnt++;
                    }
                }

                if (this.FromFrame == 0) { //if not to start from Goto keyframes (start from bigining)
                    //clear any goto rule set
                    AnimationObj.ClearGotoRule(this.FromFrame);
                }
                //remove the prevously generated animation keyframes
                if (this.KeyFramesIndex != null) {
                    _RemoveRule(this.KeyFramesIndex);
                }
                var nn = _Rand.String(8);
                //console.log("b4 "+this.KeyFramesIndex); 
                //set the css rule
                this.KeyFramesIndex = _AddRule('@keyframes ' + nn, keyframes, this.KeyFramesIndex);
                //console.log("af "+this.KeyFramesIndex);
                var dir = _CSSDirections[this.Direction];
                //get the retain attribute
                var retain = this.Retain ? "forwards" : "none"
                //set the animation
                //var elem = _IsArray(this.Target)?this.Target:[this.Target];
                this.StartTime = new Date().getTime(); //the start time
                for (var d = 0; d < this.Target.length; d++) {
                    //if to start from a particular keyframe, set the element to the style rule of the start keyframe
                    if (typeof this.Effect[this.FromFrame] != _UND) _Style(this.Target[d], this.Effect[this.FromFrame]);

                    _Style(this.Target[d], "animation:" + nn + " " + retain + " " + dur_sec + "s " + animcnt + " " + this.Delay + "s " + dir + " " + this.Ease);
                }
                //console.log("First:"+this.FirstFrameIndex+" ; Last:"+this.LastFrameIndex)

                //Start all #CSS #Trigger Timmer
                this.StartCSSTrigger();



                //if not infinite animation set the timmer to determine ending
                if (!this.Infinite) {
                    //set a timeout to determin when animation finishes
                    clearTimeout(this.AnimationFrame);

                    this.AnimationFrame = setTimeout(this.CSSAnimationEnd, mainduration);
                }
            }


        }

        //start all css triggers #Triggers CSS
        this.StartCSSTrigger = function () {
            //form triggers timeouts
            var objkeys = Object.keys(this.ProcessTrigger);
            for (const k of objkeys) {
                // if(this.ProcessTrigger.hasOwnProperty(k)){
                if (this.ProcessTrigger[k] != null) {
                    this.CSSTriggerTimmers[k] = setTimeout("_Animations['" + this.Name + "'].PerformTrigger(" + k + ")", (k * this.Duration * 1000) + (this.Delay * 1000) - this.ProgressStart);
                    // console.log("_Animations['"+this.Name+"'].PerformTrigger("+k+")"+" => "+((k*this.Duration* 1000) + (this.Delay*1000) - this.ProgressStart));
                }
                // }
            }
        }


        //clear or terminate all css trigger timmer
        //#ClearCssTriggerTimmer
        this.ClearCSSTriggerTimmer = function () {
            var objkeys = Object.keys(this.CSSTriggerTimmers);
            for (const tt of objkeys) {
                // if (this.CSSTriggerTimmers.hasOwnProperty(tt)) {
                clearTimeout(this.CSSTriggerTimmers[tt]);
                // }
            }
        }

        this.CSSTriggerTimmers = [];

        //delay
        // the index of the css rule of the animation keyframes in the aim style sheet (for css type only)
        this.KeyFramesIndex = null;

        //the css last frame rule index, for End method operation. will be set during CSS play Method
        this.LastFrameIndex = 1;

        this.FirstFrameIndex = 0;

        //#End #CSSAnimation - runs whwn css animation completes
        this.CSSAnimationEnd = function () {
            AnimationObj.State = "End";
            //reset the total time spent by the animation to 0
            AnimationObj.ProgressStart = 0;
            //if the just concluded animation starts from a Goto animation frame index
            if (AnimationObj.FromFrame > 0 && typeof AnimationObj.Effect[AnimationObj.FromFrame] != _UND) {
                //clear any goto rule set
                AnimationObj.ClearGotoRule(AnimationObj.FromFrame);
            }
            AnimationObj.FromFrame = 0; //reset the from farme (frame index where animation will start for css animation)
            if (AnimationObj.Retain) {

                var retainFrameIndex = AnimationObj.DirectionArray[AnimationObj.DirectionArray.length - 1] == "fro" ? AnimationObj.FirstFrameIndex : AnimationObj.LastFrameIndex;
                //set the target style to the last keyframe (workaround, incase the css animation forwards attribute is not considered by browser)
                _Style(AnimationObj.Target, AnimationObj.Effect[retainFrameIndex]);
            }
            //console.log('End');
            AnimationObj.OnEnd({ Target: AnimationObj.Target }); //run the OnEnd Handler
        }

        //Clear all the rule supplied from the target inline style
        this.ClearGotoRule = function (frameindex) {
            var fromRulearray = _ToStyleArray(this.Effect[frameindex]);
            // console.log(this.Effect[frameindex]);
            if (fromRulearray.length > 0) {
                var objkeys = Object.keys(fromRulearray);
                for (const sel of objkeys) {
                    //if (fromRulearray.hasOwnProperty(sel)) {
                    _Each(this.Target, function (tar) {
                        tar.style[sel] = "";
                    })

                    //}
                }
                // console.log(fromRulearray.ToStyleString());
                // _Style(AnimationObj.Target,fromRulearray.ToStyleString());
            }
            return true;
        }


        //End of CSS Animation Method and Properties
        //***************************************************** */



        //Javascript Animation Method and Properties
        //************************************************** */
        //propertiy to hold the current animation direction index
        this.DirectionIndex = 0;
        //the Animation direction(s) in array format

        //perform the animation by running it reculsively using requestAnimation Frame obj
        //#PlayJSAnimation JS Animation
        this.PlayJSAnimation = function (frameTime) {
            //console.log("StartTime:"+AnimationObj.StartTime);
            frameTime = frameTime || new Date().getTime();// the curent frameTime
            //console.log("AnimTime:"+frameTime);
            var timepass = frameTime - AnimationObj.StartTime; //the Time Passed
            //console.log("TimePass:"+timepass);
            var progress = timepass / (AnimationObj.Duration * 1000); //the progress based on the duration

            //add pause progress incase the animation has been paused once
            progress = AnimationObj.ProgressStart + progress;
            //console.log("Progress:"+progress)
            //alert(AnimationObj.StartTime);
            progress = progress > 1 ? 1 : progress;
            // var delta = _CalculateDelta(progress,AnimationObj.Effect,AnimationObj.Ease,AnimationObj.Inverse)
            var delta = AnimationObj.Delta(progress);
            //console.log("delta:"+delta)
            for (var del = 0; del < AnimationObj.Target.length; del++) {
                AnimationObj.Effect(delta, AnimationObj.Target[del]);
            }

            //perform #Trigger
            var objkeys = Object.keys(AnimationObj.ProcessTrigger);
            for (const key of objkeys) {

                // if (AnimationObj.ProcessTrigger.hasOwnProperty(key)) {
                if (key <= progress && AnimationObj.ProcessTrigger[key] != null) {
                    //AnimationObj.ProcessTrigger[key] = 
                    //perform the trigger
                    AnimationObj.PerformTrigger(key);
                }

                // }
            }

            if (progress < 1) {

                AnimationObj.AnimationFrame = _RequestFrame(AnimationObj.PlayJSAnimation);
                //update the current Progress value
                AnimationObj.Progress = progress;
            } else {
                AnimationObj.State = "End";
                //if finish set progress and ProgressStart to 0
                AnimationObj.Progress = AnimationObj.ProgressStart = 0;
                //increament Direction Index
                AnimationObj.DirectionIndex += 1;
                //check if double direction animation
                var dbldir = false;
                if (typeof AnimationObj.DirectionArray[AnimationObj.DirectionIndex] != _UND) {
                    dbldir = true;
                } else {
                    AnimationObj.DirectionIndex = 0;
                }
                //if infinte, start animation again
                if (AnimationObj.Infinite || dbldir) {
                    AnimationObj.StartJSAnimation();
                } else {
                    AnimationObj.OnEnd({ Target: AnimationObj.Target });
                }
            }
        },

            //function to recalculate delta based on inverse and ease
            this.Delta = function (progress) {
                //if progress is sent to Delta function when not during play, dont perform a fro delat calculations
                var delta = this.DirectionArray[this.DirectionIndex].toLowerCase() == "fro" && this.State == "Play" ? 1 - progress : progress;
                delta = _Easing[this.Ease] == _UND ? _Easing['ease'](delta) : _Easing[this.Ease](delta);
                return delta;
            }


        //function to #Start JS Animation
        this.StartJSAnimation = function () {
            AnimationObj.DelayStartTime = null; //reset, meaning delay has finished
            AnimationObj.DelayRem = this.Delay * 1000;
            AnimationObj.AnimationFrame = _RequestFrame(function (frameTime) {
                AnimationObj.StartTime = frameTime;
                AnimationObj.State = "Play";
                AnimationObj.PlayJSAnimation(frameTime);
            });
        },

            //JS animation property to time delay start
            this.DelayStartTime = null;

        this.DelayRem = this.Delay * 1000;

        this.DelayTimmer = null;

        //End of JavaScript Animation Methods and Properties
        //*************************************************** */




        //General Animation Methods
        //************************************************** */
        //play the animation
        //#Play
        this.Play = function (param) {
            if (this.State == "Play") {
                console.log("Operation Denied: _Animation(" + this.Name + ") is playing");
                return;
            }
            //if animation properties,methods or event handler are send during Play call
            //loop trough, and set appropriately
            if (typeof param != _UND) {
                var objkeys = Object.keys(param);
                for (var key of objkeys) {
                    //if (param.hasOwnProperty(key)) {
                    if (typeof this[key] != _UND) {
                        this[key] = param[key];
                    }
                    //}
                }
            }
            //reset the animation process trigger #Reset Process #Trigger
            if (this.State != "Pause" && this.State != "GotoPause") {
                this.ResetProgressTrigger();
            }

            // run the onstart handler
            //#OnStart Animation
            this.OnStart({ Target: this.Target });

            if (_IsFunction(this.Effect)) {//if JS Animation
                if (this.Delay > 0 && this.State != "Pause" && this.State != "GotoPause") {
                    if (this.State == "End") { //if the last operation before play is clicked if End operation
                        //make the target move to the initial state, indicating animation as started but in a delay (simulating thesame effect of CSS animation)
                        var delta = this.Delta(0);
                        for (var del = 0; del < this.Target.length; del++) {
                            this.Effect(delta, this.Target[del]);
                        }

                    }
                    this.State = "Play";
                    this.DelayStartTime = new Date().getTime();
                    this.DelayTimmer = setTimeout(this.StartJSAnimation, this.Delay * 1000)
                } else {
                    if (this.DelayStartTime != null) {//if paused when in delay
                        //continue delay
                        this.State = "Play";
                        this.DelayStartTime = new Date().getTime();
                        this.DelayTimmer = setTimeout(this.StartJSAnimation, this.DelayRem)
                    } else {
                        this.StartJSAnimation();
                    }
                }
            } else if (_IsObject(this.Effect)) { //if CSS Animation with keyframes set
                this.StartTime = new Date().getTime();
                this.PlayCSSAnimation();
                //#log
                // console.log("CSS Animation Started in Gen Play at: StartTime="+this.StartTime);
            } else {//expected to be string. if inbuild effect
                this.Effect = this.Effect.toString();
                //check if not exist and use default
                this.Effect = typeof _AnimationEffects[this.Effect] == _UND ? _AnimationEffects['FadeIn'] : _AnimationEffects[this.Effect];
                this.StartTime = new Date().getTime();
                this.PlayCSSAnimation();
            }
        }

        //#Pause - perform pause
        this.Pause = function () {
            // this.Target.style.animationPlayState = "paused"
            if (this.State == "Play") {
                //check if animation type is js
                if (this.Type == "JS") {
                    //if in delay state, 
                    if (this.DelayStartTime != null) {
                        //stop the delay timmer
                        clearTimeout(this.DelayTimmer);
                        this.DelayRem = (this.DelayRem) - (new Date().getTime() - this.DelayStartTime); // keep the total delay time remaining

                    } else {
                        //cancel the animation frame
                        _CancelFrame(this.AnimationFrame);
                        //keep the current animation progress in ProgressStart. so that it can be use to start animation where it paused
                        this.ProgressStart = this.Progress;
                    }
                } else { //if css
                    _Each(this.Target, function (tar) {
                        tar.style.animationPlayState = "paused";
                    });

                    //stop the end of animation timmer
                    clearTimeout(this.AnimationFrame);
                    //stop all trigger timmer
                    this.ClearCSSTriggerTimmer();

                    //get the time spent so far
                    //acummulate the total time spent by the animation in playing state
                    this.ProgressStart += new Date().getTime() - this.StartTime;
                }

                this.OnPause();
                //set animation object state to pause
                this.State = "Pause";
                return true;
            }
        }

        //#Goto - goto a particular frame / level
        this.Goto = function (frameIndex) {
            //Not allowed for infinity or bi-directional animation
            if (this.Infinite && frameIndex > 0) {
                console.log("Jump/End not allowed during Infinite Animation");
                return;
            }
            if (this.DirectionArray.length > 1 && frameIndex > 0 && frameIndex < 1) {
                console.log("Jump not allowed during Bi-Directional Animation");
                return;
            }

            frameIndex = _ToFloat(frameIndex);
            //terminate the current animation
            //cancel the animation frame


            //if current state is Play, replay the animation
            if (this.State == 'Play') {
                if (this.Type == "JS") { //js animation type
                    _CancelFrame(this.AnimationFrame);
                    this.State = "Pause";//this will enable the replay
                    //reset the Starting value of progress
                    this.ProgressStart = frameIndex;

                    this.Play();
                } else {
                    this.State = "GotoPause";
                    for (var n = 0; n < this.Target.length; n++) {
                        this.Target[n].style.animationPlayState = "paused";
                        _Style(this.Target[n], this.Effect[frameIndex]);
                    }

                    this.FromFrame = frameIndex; //specify which frame to start animation from
                    this.Play(); //play the animation
                }
            } else {

                if (this.Type == "JS") { //js animation type
                    //stop the delay timmer
                    clearTimeout(this.DelayTimmer);
                    _CancelFrame(this.AnimationFrame);
                    this.ProgressStart = frameIndex == 1 ? 0 : frameIndex; //if finish animation reset the PrograaStart to 0, so that when replay it starts from the biginning
                    if (this.ProgressStart == 0) { //if stoped or end
                        this.DirectionIndex = 0; //reset the animation direction
                    }
                    var delta = this.Delta(frameIndex);
                    for (var g = 0; g < this.Target.length; g++) {
                        this.Effect(delta, this.Target[g]);
                    }


                } else { //css animation type
                    clearTimeout(this.AnimationFrame);
                    //alert(this.Target);
                    _Each(this.Target, function (tar) { tar.style.animation = ""; })

                    //remove the prevously generated animation keyframes
                    if (this.KeyFramesIndex != null) {
                        _RemoveRule(this.KeyFramesIndex);
                        this.KeyFramesIndex = null;
                    }

                    //if to goto a particular key frame.
                    //NB: the distination keyframe is not part of the user define keyfarme in Effect, the last keyframe will be used
                    //if the keyframe sent does not exist use the last keyframe, i.e go to the last frame
                    frameIndex = typeof this.Effect[frameIndex] == _UND ? this.LastFrameIndex : frameIndex;
                    //set the inline style attribute of the target to the Goto frame index style rule
                    for (var s = 0; s < this.Target.length; s++) {
                        _Style(this.Target[s], this.Effect[frameIndex]);
                    }

                    //}
                    //reset the from frame to the frameindex
                    if (frameIndex == 0 || frameIndex == 1) {
                        this.FromFrame = 0;

                    } else {
                        this.FromFrame = frameIndex
                    }
                    //reset the progressStart time
                    this.ProgressStart = 0
                    //}
                }
                this.OnEnd({ Target: this.Target });
            }
        }


        //#Stop Perform Stop - will stop the animation and return back to the starting point
        this.Stop = function () {
            // this.Target.style.animationPlayState = "";return;
            /* if(this.State != "Play" && this.State != "Pause"){
                 console.log("Aim Animation: Stop can only be performed during Play");
                 return;
             }*/
            this.State = 'Stop';

            this.Goto(0);
        }

        //#End Perform End - will end the animation, by going to the last frame
        this.End = function () {
            this.State = 'End';
            this.Goto(1);
        }
        //#PerformTrigger #Trigger => function to start trigger animation or run function or goto keyframe as required
        this.PerformTrigger = function (keyframe) {
            var trigger = this.ProcessTrigger[keyframe] || null;
            if (trigger == null) return;
            //if trigger is array, perform the function on each item of the array
            if (_IsArray(trigger)) {
                for (var t = 0; t < trigger; t++) {
                    this.PerformTrigger(trigger[t]);
                }
                return;
            }

            //if trigger type if function, run the function
            if (_IsFunction(trigger)) { trigger(); }

            //if trigger is number, perform a Goto
            if (_IsNumber(trigger)) { this.Goto(trigger) }

            //if is string get the animation object
            var triggeranimobj;
            if (_IsString(trigger)) {
                triggeranimobj = _Animations[trigger] || null;
            } else { //if it is object
                triggeranimobj = trigger
            }

            if (triggeranimobj != null && triggeranimobj instanceof _Animation) {
                //set the trigger animation's Timeline to the current animation timeline. cos, any animation that is triggered by another animation is automatically in the same timeline
                triggeranimobj.Timeline = this.Timeline;
                triggeranimobj.Play();
            }

            //remove the trigger
            this.ProcessTrigger[keyframe] = null;
            delete this.ProcessTrigger[keyframe];


        }

        //#Reset Process #Trigger Object
        this.ResetProgressTrigger = function () {
            this.ProcessTrigger = {};
            var trigkeys = Object.keys(this.Trigger);
            for (var rt of trigkeys) {
                // if (this.Trigger.hasOwnProperty(rt)) {
                this.ProcessTrigger[rt] = this.Trigger[rt];

                //}
            }
        }

        //#Timeline Name if attached to a timeline
        this.Timeline = "";

        //End of General Animation Methods
        //************************************************** */




        //add this animation object to aim global animations array
        //NB: if user defined name exist, it will replace it
        _Animations[this.Name] = this;

    }

    //Aim #Timelines arrays
    _Timelines = [],

        //Aim Timeline Object
        //#Timeline
        _Timeline = function () {
            //expected argurments are valid Aim Animation Objects

            //Timeline Name
            var randname = _Rand.Number(10);
            while (typeof _Timelines[randname] != _UND) {
                randname = _Rand.Number(10);
            }
            this.Name = randname;

            //Internal array holding all addeded animation objects
            this.Animations = { length: 0 };

            //Method to #AddAnimation Objects
            this.AddAnimation = function () {
                //expected argurments are valid Aim Animation Objects
                if (arguments.length < 1) return;

                for (var d = 0; d < arguments.length; d++) {
                    var animobj = arguments[d];
                    if (_IsString(animobj) && typeof _Animations[animobj] != _UND) {
                        animobj = _Animations[animobj];
                    }

                    if (animobj instanceof _Animation) {
                        animobj.Timeline = this.Name;
                        this.Animations[animobj.Name] = animobj;
                        this.Animations.length++;
                    }
                }
                //console.log(this.Animations.length)
            }

            //Determine if Timeline is setup
            this.IsSetup = false

            //the Timeline State
            this.State = "Stop"; //Play | Pause

            //#Play the Timeline
            this.Play = function (setupstr) {
                //check if the current state is Pause
                if (this.State == "Pause") {
                    //loop trough all Timeline animations and play all paused onces
                    var akeys = Object.keys(_Animations);
                    for (var akey of akeys) {
                        if (_Animations[akey].Timeline == this.Name && _Animations[akey].State == "Pause") {
                            _Animations[akey].Play();

                        }
                    }
                    this.State = "Play";
                    return;
                }
                //check if no setup found
                if ((typeof setupstr == _UND || _Trim(setupstr) == "") && !this.IsSetup) this.PlayAll();
                //if setup string exist run setup
                var setup = (typeof setupstr != _UND && _Trim(setupstr) != "") ? this.Setup(setupstr) : true;
                if (setup) {
                    _Animations[this.Starter].Play();
                } else {
                    console.log("Invalid Aim Timeline Setup");
                }
            }

            //#PlayAll Animation
            this.PlayAll = function () {
                var animkeys = Object.keys(this.Animations);
                for (const key of animkeys) {
                    if (this.Animations[key] instanceof _Animation) {
                        this.Animations[key].Play();
                    }
                }
            }

            //#Starter, the first animation to play
            this.Starter = "";

            //#Setup the Timeline
            this.Setup = function (setupstr) {
                //perform setup
                //format AnimationName1:TriggerTime=>AnimationName2:TriggerTime=>......=>AnimationNameN
                this.IsSetup = false;
                if (typeof setupstr == _UND || _Trim(setupstr) == "" || !_IsString(setupstr)) return false;

                //get individual animations
                var Anims = setupstr.split("=>");
                if (Anims.length > 0) {
                    for (var s = 0; s < Anims.length; s++) {//loop through all the animation
                        var animtrig = Anims[s]; //get the animation details (Name:Trigger)
                        var animtrigArr = animtrig.split(":");//Split to get the name and triggerTime
                        var animName = animtrigArr[0]; //get Name
                        if (s == 0) this.Starter = animName; //if the first animation details, set the animation name as the first animation to run by the Timeline
                        //get the next AnimationName if exist
                        var nxtAnimName = (typeof Anims[s + 1] != _UND) ? Anims[s + 1].split(":")[0] : "";
                        //get the trigger time if set, else use 1
                        var triggerTime = (typeof animtrigArr[1] != _UND) ? _ToFloat(animtrigArr[1]) : 1;
                        //set the trigger
                        if (nxtAnimName != "") { //if next animation name exist
                            //set the animation trigger to the next animation name
                            _Animations[animName].Trigger[triggerTime > 1 ? 1 : triggerTime] = nxtAnimName;
                        }

                    }
                    //set the IsSetup to true (meaning the setup done)
                    this.IsSetup = true;
                    return true;
                }
                return false;
            }

            //#Pause Timeline
            this.Pause = function () {
                //loop through all the timeline animation and pause them
                var cnt = 0;
                var animkeys = Object.keys(_Animations);
                for (const akey of animkeys) {
                    if (_Animations[akey].Timeline == this.Name) {
                        _Animations[akey].Pause();
                        cnt++;
                    }
                }
                if (cnt > 0) this.State = "Pause";
            }

            //#Stop Timeline
            this.Stop = function () {
                //loop through all the Timeline animation and stop them
                var cnt = 0;
                var animkeys = Object.keys(_Animations);
                for (const akey of animkeys) {

                    //if(_Animations[akey])console.log(akey + ":"+_Animations[akey].Timeline)
                    if (_Animations[akey].Timeline == this.Name) {
                        _Animations[akey].Stop();
                        cnt++;
                    }
                }
                if (cnt > 0) this.State = "Stop";
            }

            //#Add animations if provided during initialization
            if (arguments.length > 0) {
                for (var ar = 0; ar < arguments.length; ar++) {
                    this.AddAnimation(arguments[ar]);
                }
            }
        }

    _QuickAnimation = function (elem, param) {
        if (typeof elem == _UND) return;
        elem = _RP(elem);

        if (!_IsFound(elem)) return;
        param = param || {};
        param.Target = elem;
        var eff = param.Effect || "FadeIn";
        var animNameType = "aim-Quick";
        if (typeof param.Name == _UND || _Trim(param.Name) == "") {
            if (_IsString(eff)) {
                animNameType += "-" + eff;
            } else {
                animNameType += "-Anim";
            }
            param.Name = animNameType + "-" + elem.ID;
        }

        var anim = typeof _Animations[param.Name] == _UND ? new _Animation(param) : _Animations[param.Name];

        anim.Play();
    }
    _ZoomIn = function (elem) {
        _QuickAnimation(elem, {
            Effect: "ZoomIn",
            Duration: 0.8, OnStart: function (obj) { obj.Target[0].style.display = 'block' }, OnEnd: function (obj) {//obj.Target.Style = "transform: scale(0)"}});
            }
        })
    }
    _ZoomOut = function (elem) {
        _QuickAnimation(elem, {
            Effect: "ZoomOut",
            Duration: 0.8, OnEnd: function (obj) {
                obj.Target[0].style.display = 'none';//obj.Target.Style = "transform: scale(1)"}});
            }
        })
    }
    _FadeIn = function (elem) {
        _QuickAnimation(elem, {
            Effect: "FadeIn",
            Duration: 0.8, OnStart: function (obj) { obj.Target[0].style.display = 'block' }
        });
    }

    _FadeOut = function (elem) {
        _QuickAnimation(elem, {
            Effect: "FadeOut",
            Duration: 0.8, OnEnd: function (obj) { obj.Target[0].style.display = 'none' }
        });
    }
    _LeftIn = function (elem) {
        _QuickAnimation(elem, {
            Effect: "LeftIn",
            Duration: 0.8
        });
    }
    _LeftOut = function (elem) {
        _QuickAnimation(elem, {
            Effect: "LeftOut",
            Duration: 0.8
        });
    }
    _RightIn = function (elem) {
        _QuickAnimation(elem, {
            Effect: "RightIn",
            Duration: 0.8
        });
    }
    _RightOut = function (elem) {
        _QuickAnimation(elem, {
            Effect: "RightOut",
            Duration: 0.8
        });
    }
    _TopIn = function (elem) {
        _QuickAnimation(elem, {
            Effect: "TopIn",
            Duration: 0.8
        });
    }
    _TopOut = function (elem) {
        _QuickAnimation(elem, {
            Effect: "TopOut",
            Duration: 0.8
        });
    }
    _BottomIn = function (elem) {
        _QuickAnimation(elem, {
            Effect: "BottomIn",
            Duration: 0.8
        });
    }
    _BottomOut = function (elem) {
        _QuickAnimation(elem, {
            Effect: "BottomOut",
            Duration: 0.8
        });
    }

    var Style = (function () {
        // Create the <style> tag
        var style = document.createElement("style");
        // WebKit hack :(
        //style.appendChild(document.createTextNode(""));
        // Add the <style> element to the page
        document.head.appendChild(style);
        //style.sheet.insertRule("body::before{content:'Loading...';position:absolute;width:100%;height:100%;z-index:10000;background-color:#EEE;font-size:1.3em;left: 0px; top: 0px; display: block}");
        return style;
    })();


    //#AIM PRINTER #PRINTER #PrintPDF - the aim pdf printer object
    _Printer = {
        //Hold all the Printer Object Animations
        Animations: [],
        //Hold all the Printer Timeline
        Timelines: [],
        //Printer #Status #State
        State: "Finish",
        //the current loading parameters
        Settings: {
            Default: { //fixed settings, will be use to reset
                PaperType: "A4",
                Orientation: "P",//P-Portrait, L-Landscape
                FontSize: 10,
                MarginLeft: 4,
                MarginRight: 4,
                MarginTop: 4,
                MarginBottom: 4,
                Src: "", //must be relative to the calling page (script loading aim)
                Title: "Untitled Document",
                WaterMark: "",
                Data: "",
                Folder: ""
            },
            Process: {} //will contain the current settings
        },

        FormURL: function () {
            var setstr = "";
            if (typeof _Printer.Settings.Process['PaperType'] != _UND) setstr += "&paper=" + _Printer.Settings.Process['PaperType'];
            if (typeof _Printer.Settings.Process['Orientation'] != _UND) setstr += "&orientation=" + _Printer.Settings.Process['Orientation'];
            if (typeof _Printer.Settings.Process['FontSize'] != _UND) setstr += "&fontsize=" + _Printer.Settings.Process['FontSize'];
            if (typeof _Printer.Settings.Process['MarginLeft'] != _UND) setstr += "&ML=" + _Printer.Settings.Process['MarginLeft'];
            if (typeof _Printer.Settings.Process['MarginRight'] != _UND) setstr += "&MR=" + _Printer.Settings.Process['MarginRight'];
            if (typeof _Printer.Settings.Process['MarginTop'] != _UND) setstr += "&MT=" + _Printer.Settings.Process['MarginTop'];
            if (typeof _Printer.Settings.Process['MarginBottom'] != _UND) setstr += "&MB=" + _Printer.Settings.Process['MarginBottom'];
            //if(setstr == "")setstr = "&";


            setstr = _Printer.Settings.Process['Src'] + setstr;
            return setstr;
        },
        //Refresh Timmer
        RefreshTimmer: null,
        //#Refresh Printer
        Refresh: function (delay) {
            if (typeof _Printer.Settings.Process['Src'] == _UND) return;
            //if no delay sent set delay to false
            delay = delay || false;
            //clear the timmer
            if (_Printer.RefreshTimmer != null) clearTimeout(_Printer.RefreshTimmer);
            if (delay) { //if delay required
                //run refresh after 300 miliseconds
                _Printer.RefreshTimmer = setTimeout("_Printer.Refresh()", 300);
                return;
            }
            _Printer.Timelines['aim-printpdf-loading-timeline'].Stop();
            _Printer.Timelines['aim-printpdf-loading-timeline'].Play('aim-printpdf-loading:0.2=>aim-printpdf-loading1:0.3=>aim-printpdf-loading2:0.3=>aim-printpdf-loading3');
            _("aim-printpdf-footer-status").textContent = "Generating View ...";
            //convert settings to json
            var nset = JSON.stringify(_Printer.Settings.Process);
            var setstr = _Printer.FormURL();

            //aim-printer-generate
            //  _('aim-printpdf-iframe').src = _Dom.Loader.Dir+"aim.php?aim-printer-generate=1&aim-version="+_.Version+"&aim-printer-settings="+escape(nset)+"&aim-dir="+_Dom.Loader.Dir+"&aim-root="+_Dom.Loader.Root;
            _('aim-printpdf-iframe').src = setstr;
            //  _('aim-printpdf-iframe').data = setstr;
            _Printer.State = "Refresh";
        },
        //#load the printer
        Load: function (settings) {
            settings = settings || {};
            var defkeys = Object.keys(_Printer.Settings.Default);
            //set the process settings (parameters), use the default attribute if not set
            for (const key of defkeys) {
                //  if (_Printer.Settings.Default.hasOwnProperty(key)) {
                var setval = typeof settings[key] != _UND ? settings[key] : _Printer.Settings.Default[key];
                _Printer.Settings.Process[key] = setval;

                //recalculate display objects state in sidebar
                //*********************************** */
                var uval = _Printer.Settings.Process[key];

                if (key == "PaperType") {//Paper Type
                    _('aim-printpdf-papertype').Value = uval;
                } else if (key == "Orientation") {//Paper Orientation
                    //get the other button by ID
                    var pobj = _('aim-printpdf-portrait'),
                        lobj = _('aim-printpdf-landscape');
                    var objs = uval.toLowerCase() == 'p' ? [pobj, lobj] : [lobj, pobj];

                    if (objs[0].classList.contains("w3-opacity")) {
                        objs[0].className = objs[0].className.replace(" w3-opacity w3-card-4 w3-hover-greyscale", "");
                        objs[1].className = objs[1].className + " w3-opacity w3-card-4 w3-hover-greyscale";
                    }
                } else if (key == "FontSize") {//font size
                    uval = _ToInteger(uval);
                    uval = uval < 5 ? 5 : uval > 42 ? 42 : uval;
                    var lf = Math.floor(((uval - 5) * 210) / 37);
                    _('aim-printpdf-fontslider').style.left = lf + "px";
                    _('aim-printpdf-fontsize').textContent = uval;
                } else if (key == "MarginLeft") { //Margin Left
                    uval = _ToInteger(uval);
                    uval = uval < 4 ? 4 : uval > 40 ? 40 : uval;
                    var lf = Math.floor((50 * (uval - 4)) / 36) + 30;
                    _('aim-printpdf-marginline-left').style.left = lf + "px";
                    _('aim-printpdf-marginvalue-left').textContent = uval;
                } else if (key == "MarginTop") { //Margin Top
                    uval = _ToInteger(uval);
                    uval = uval < 4 ? 4 : uval > 40 ? 40 : uval;
                    var tp = Math.floor((50 * (uval - 4)) / 36) + 30;
                    _('aim-printpdf-marginline-top').style.top = tp + "px";
                    _('aim-printpdf-marginvalue-top').textContent = uval;
                } else if (key == "MarginRight") { //Margin Right
                    uval = _ToInteger(uval);
                    uval = uval < 4 ? 4 : uval > 40 ? 40 : uval;
                    var lf = 220 - Math.floor((50 * (uval - 4)) / 36);
                    _('aim-printpdf-marginline-right').style.left = lf + "px";
                    _('aim-printpdf-marginvalue-right').textContent = uval;
                } else if (key == "MarginBottom") { //Margin Bottom
                    uval = _ToInteger(uval);
                    uval = uval < 4 ? 4 : uval > 40 ? 40 : uval;
                    var tp = 220 - Math.floor((50 * (uval - 4)) / 36);
                    _('aim-printpdf-marginline-bottom').style.top = tp + "px";
                    _('aim-printpdf-marginvalue-bottom').textContent = uval;
                }
                //recalculate display objects state in sidebar Ends
                //*********************************** */
                // }
            }
            //convert settings to json
            var nset = JSON.stringify(_Printer.Settings.Process);
            //   _ZoomIn(_('aim-printpdf'));
            var printt = _('aim-printpdf');
            //alert('sss');
            printt.classList.remove('zoomOutDown');
            printt.classList.add('zoomInDown');
            if (printt.style.display == 'none') printt.style.display = 'block';
            _Printer.Timelines['aim-printpdf-loading-timeline'].Play('aim-printpdf-loading:0.2=>aim-printpdf-loading1:0.3=>aim-printpdf-loading2:0.3=>aim-printpdf-loading3');
            _("aim-printpdf-footer-status").textContent = "Generating View ...";
            var setstr = _Printer.FormURL();
            //aim-printer-generate
            //   _('aim-printpdf-iframe').src = _Dom.Loader.Dir+"aim.php?aim-printer-generate=1&aim-version="+_.Version+"&aim-printer-settings="+escape(nset)+"&aim-dir="+_Dom.Loader.Dir+"&aim-root="+_Dom.Loader.Root;
            _('aim-printpdf-iframe').src = setstr;
            //   _('aim-printpdf-iframe').data = setstr;
            // _('aim-printpdf-iframe').src = "http://live/epdevelop/portal/Admin/Slip.php?dl=0&paper=A4&orientation=P&fontsize=10&ML=4&MT=4&MR=4&MB=14&fn=Entrance_EP/TEST/078.pdf&folder=PUTME&RegNo=EP/TEST/078&RegID=1&paper=A4&orientation=P&MT=4&MB=14";
            _Printer.State = "Load";

        },

        //#download the generated pdf file
        Download: function () {
            //alert('ddd');
            //convert settings to json
            var nset = JSON.stringify(_Printer.Settings.Process);
            // alert(nset);
            // _Printer.Timelines['aim-printpdf-loading-timeline'].Play('aim-printpdf-loading:0.2=>aim-printpdf-loading1:0.3=>aim-printpdf-loading2:0.3=>aim-printpdf-loading3');
            // _("aim-printpdf-footer-status").textContent = "Preparing Download ...";
            //aim-printer-generate
            var setstr = _Printer.FormURL() + "&dl=1";
            _('aim-printpdf-iframedl').src = setstr;
            //_Printer.State = "Download";
        },

        //When view loaded completed
        //Called by the iframe onload event handler
        Finish: function (download) {
            download = download || false;
            //Stop the loading animation
            _Printer.Timelines['aim-printpdf-loading-timeline'].Stop();
            _("aim-printpdf-footer-status").textContent = _Printer.Settings.Process["Title"];
            //set printer status
            _Printer.State = "Finish";

            if (!download) {
                //alert(_Printer.Settings.Process["PaperType"]);
                //set status infos
                _('aim-footer-papertype').textContent = _Printer.Settings.Process["PaperType"];
                _('aim-footer-orientation').textContent = _Printer.Settings.Process["Orientation"] == "L" ? "Landscape" : "Portrait";
                _('aim-footer-fontsize').textContent = _Printer.Settings.Process["FontSize"];
                _('aim-footer-margintop').textContent = _Printer.Settings.Process["MarginTop"];
                _('aim-footer-marginright').textContent = _Printer.Settings.Process["MarginRight"];
                _('aim-footer-marginleft').textContent = _Printer.Settings.Process["MarginLeft"];
                _('aim-footer-marginbottom').textContent = _Printer.Settings.Process["MarginBottom"];
                _('aim-printer-version').textContent = "EP V3 PrintPDF ";
            }
        },

        //close the printer
        Close: function () {
            _Printer.Timelines['aim-printpdf-loading-timeline'].Stop();
            _('aim-printpdf').classList.remove('zoomInDown');
            _('aim-printpdf').classList.add('zoomOutDown');
        },
        //Array holding the loading animations
        LoadingTM: null,
        //Initialize the printer object
        //NB:must be registered in the Aim InitManager
        Init: function () {

            //get the markup, append to the body the perform initization
            //opertation should be done only once
            //aim-printerpdf object is use to determine if all markup has been loaded
            /* if(!_IsFound(_('aim-printpdf'))){ //if not already added */
            //get from aim server
            /* _.Post({ 
                Action:_Dom.Loader.Dir+"aim.php",
                Data:{"aim-markup":1,"aim-root":_Dom.Loader.Root},
                OnComplete:function(res,url,param){*/
            // alert(res)
            // return;
            /* var printmarkup = "";
             res = _Server(res); //proccess server responce
             if(res.HasError){ //if error found
               _.Log(res.Error.Code + " - " + res.Error.Line + " " + res.Error.Message);
              // alert(JSON.stringify(res.Error))
              if(res.Error.Abort){
                 document.body.className += " aim-error"; 
                  return;
              }
             }else{
                 printmarkup = res.Markup;
             }
             
             
           if(_Trim(printmarkup) != ""){ //if markup gotten
             //append it to the documment element
             
             document.body.insertAdjacentHTML('afterbegin',printmarkup); */
            //pefrom initialize the printer elements

            //Aim Printer (#PrintPDF) Element Initialization
            //************************************** */
            //Loadining Animation 

            for (var aa = 1; aa < 4; aa++) {
                _Printer.Animations['aim-printpdf-loading' + aa] = new _Animation({
                    Name: 'aim-printpdf-loading' + aa,
                    Target: 'aim-printpdf-loading' + aa,
                    Effect: { 0: "top:0;opacity:0.5", 1: "top:40px;opacity:1" },
                    Duration: 0.5,
                    Direction: "to-fro",
                    Infinite: true
                });
            }
            _Printer.Animations['aim-printpdf-loading'] = new _Animation({
                Name: 'aim-printpdf-loading',
                Target: 'aim-printpdf-loading',
                Effect: 'FadeIn',
                Duration: 0.5
            });
            _Printer.Timelines['aim-printpdf-loading-timeline'] = new _Timeline('aim-printpdf-loading', 'aim-printpdf-loading1', 'aim-printpdf-loading2', 'aim-printpdf-loading3');
            //console.log(_('aim-printpdf-orientation'));
            _Assign('click', _('aim-printpdf-orientation'), function (e) {
                e.stopPropagation();
                //get the object(button) clicked
                var obj = e.currentTarget;
                //console.log(obj);
                //get the other button by ID

                var obj2 = obj.id == 'aim-printpdf-portrait' ? 'aim-printpdf-landscape' : 'aim-printpdf-portrait';
                //alert(obj.id);
                //alert(obj2);
                obj2 = _(obj2);

                if (obj.classList.contains("w3-opacity")) {
                    obj.className = obj.className.replace(" w3-opacity w3-card-4 w3-hover-greyscale", "");
                    obj2.className = obj2.className + " w3-opacity w3-card-4 w3-hover-greyscale";
                    //Reset the Orientation and refresh printer
                    _Printer.Settings.Process['Orientation'] = obj.id == 'aim-printpdf-portrait' ? 'P' : 'L';
                    //console.log(_Printer.Settings.Process['Orientation']);
                    //#Refresh Printer
                    _Printer.Refresh(true);
                }
            });

            //The Display and Download #iframe #onload eventhandler
            _Assign('load', _('aim-printpdf-iframe'), function (e) {
                e.stopPropagation();
                _Printer.Finish();
            });

            //the #PrintPDF #fontsize slider drag operation (setting font size)
            _Drag.Element(_('aim-printpdf-fontslider'), {
                Direction: "X", //move in X axis only
                Restricted: true, //confined drag movement withen its container only
                OnDrag: function (e) {//calculate the font size as user drags the slider (minimum value = 5, maximum value = 40)
                    //NB: the max drag left value of slider is 210
                    _('aim-printpdf-fontsize').textContent = Math.floor((e.Left / 210) * 37) + 5;
                },
                OnFinish: function () { //perform the printer refresh after fontsize set (drag ends)
                    //reset the Font size and refresh printer
                    _Printer.Settings.Process['FontSize'] = _ToInteger(_('aim-printpdf-fontsize').textContent)
                    //#Refresh Printer
                    _Printer.Refresh(true);
                }
            });

            //the #PrintPDF #margintop line drag operation (setting margin top)
            _Drag.Element(_('aim-printpdf-marginline-top'), {
                Direction: "Y", //Move in Y axis only
                MaximumY: 80, //the maximum Y axis (top value) to reach
                MinimumY: 30, //the minimum Y axis (top value) to reach
                OnDrag: function (e) { //calculate the margin top and display it as user drags the line
                    //the move area (length) 80 - 30 = 50
                    //Minimum margin top = 4
                    //Maximum margin top = 40
                    _('aim-printpdf-marginvalue-top').textContent = Math.floor(((e.Top - 30) / 50) * 36) + 4
                },
                OnFinish: function () { //perform the printer refresh after margin top set (drag ends)
                    //reset the Margin Top and refresh printer
                    _Printer.Settings.Process['MarginTop'] = _ToInteger(_('aim-printpdf-marginvalue-top').textContent)
                    //#Refresh Printer
                    _Printer.Refresh(true);
                }
            });

            //the #PrintPDF #marginleft line drag operation (setting margin left)
            _Drag.Element(_('aim-printpdf-marginline-left'), {
                Direction: "X", //Move in X axis only
                MaximumX: 80, //the maximum X axis (left value) to reach
                MinimumX: 30, //the minimum X axis (left value) to reach
                OnDrag: function (e) {//calculate the margin left and display it as user drags the line
                    //the move area (length) 80 - 30 = 50
                    //Minimum margin left = 4
                    //Maximum margin left = 40
                    _('aim-printpdf-marginvalue-left').textContent = Math.floor(((e.Left - 30) / 50) * 36) + 4
                },
                OnFinish: function () {//perform the printer refresh after margin left set (drag ends)
                    //reset the Margin Left and refresh printer
                    _Printer.Settings.Process['MarginLeft'] = _ToInteger(_('aim-printpdf-marginvalue-left').textContent)
                    //#Refresh Printer
                    _Printer.Refresh(true);
                }
            });

            //the #PrintPDF #marginright line drag operation (setting margin right)
            _Drag.Element(_('aim-printpdf-marginline-right'), {
                Direction: "X",//Move in X axis only
                MaximumX: 250 - 30,//the maximum X axis (left value) to reach (the total container width - the initial gap to the right)
                MinimumX: 250 - 30 - 50, //the minimum X axis (left value) to reach (the total container width - the initial gap to the right - the total move value(50))
                OnDrag: function (e) {//calculate the margin right and display it as user drags the line
                    //the move area (length) = 50
                    //Minimum margin right = 4
                    //Maximum margin right = 40
                    //the maximum X cordinate = 250 - 30 = 220
                    _('aim-printpdf-marginvalue-right').textContent = Math.floor(((220 - e.Left) / 50) * 36) + 4
                },
                OnFinish: function () {//perform the printer refresh after margin right set (drag ends)
                    //reset the Margin Right and refresh printer
                    _Printer.Settings.Process['MarginRight'] = _ToInteger(_('aim-printpdf-marginvalue-right').textContent)
                    //#Refresh Printer
                    _Printer.Refresh(true);
                }
            });

            //the #PrintPDF #marginbottom line drag operation (setting margin bottom)
            _Drag.Element(_('aim-printpdf-marginline-bottom'), {
                Direction: "Y",//Move in Y axis only
                MaximumY: 250 - 30,//the maximum Y axis (top value) to reach (the total container height - the initial gap to the top)
                MinimumY: 250 - 30 - 50,//the minimum Y axis (top value) to reach (the total container height - the initial gap to the top - the total move value(50))
                OnDrag: function (e) {//calculate the margin bottom and display it as user drags the line
                    //the move area (length) = 50
                    //Minimum margin bottom = 4
                    //Maximum margin bottom = 40
                    //the maximum Y cordinate = 250 - 30 = 220
                    _('aim-printpdf-marginvalue-bottom').textContent = Math.floor(((220 - e.Top) / 50) * 36) + 4
                },
                OnFinish: function () {//perform the printer refresh after margin bottom set (drag ends)
                    //reset the Margin Bottom and refresh printer
                    _Printer.Settings.Process['MarginBottom'] = _ToInteger(_('aim-printpdf-marginvalue-bottom').textContent)
                    //#Refresh Printer
                    _Printer.Refresh(true);
                }
            });

            //The onchange event handler of the paper type dropdown (select)
            _Assign('change', _('aim-printpdf-papertype'),
                function (e) {
                    e.stopPropagation();
                    //reset the papertype and refresh
                    _Printer.Settings.Process['PaperType'] = e.currentTarget.options[e.currentTarget.selectedIndex].value;
                    //#Refresh Printer
                    _Printer.Refresh(true);
                });

            //create the slide-in/out animation of the #PrintPdf sidebar #aimPrintPdfSidebarAnim
            _Printer.Animations['aim-printpdf-sidebar'] = new _Animation({
                Target: 'aim-printpdf-sidebar', //the id of the sidebar
                Effect: 'LeftIn', //move from left effect
                Name: "aim-printpdf-sidebar-anim", //the animation name (to be use in timeline to identify animation)
                Duration: 0.5 //the animation duration
            });

            //create the slide-in/out animation of the #PrintPdf small sidebar (for small screen) #aimPrintPdfSmallMenuAnim
            _Printer.Animations['aim-printpdf-small-menu'] = new _Animation({
                Target: 'aim-printpdf-small-menu',
                Effect: 'LeftIn',
                Name: "aim-printpdf-small-menu-anim",
                Duration: 0.5
            });

            //create the #PrintPdf Timeline that controls the two animation #printPdfTM
            _Printer.Timelines['aim-printpdf-sidebar-timeline'] = new _Timeline('aim-printpdf-sidebar', 'aim-printpdf-small-menu');

            //Assign function to the onclick event of the button(in the small sidebar) to show the main sidebar
            //the function animates out the small sidebar and animate in the main sidebar using the Timeline #printPdfTM
            _Assign('click', _('aim-printpdf-show-sidebar'), function () {
                //e.stopPropagation();
                var sidebarAnim = _Printer.Animations['aim-printpdf-sidebar'], smallSidebarAnim = _Printer.Animations['aim-printpdf-small-menu'];
                sidebarAnim.Direction = "to"; //set the main sidebar animation direction to forward (move in)
                smallSidebarAnim.Direction = "fro";//set the small sidebar animation direction to reverse (move out)
                sidebarAnim.OnEnd = function () {
                    _Style(_('aim-printpdf-sidebar'), "left:0px;opacity:1");
                }; //reset the left and opacity of the main sidebar
                //set the OnStart to remove the w3 hide class on it so that it will be visible when animated In
                sidebarAnim.OnStart = function () {
                    var cn = _('aim-printpdf-sidebar'); //get the sidebar using aim finder
                    //clear the w3 hide clases
                    cn.className = cn.className.replace(" w3-hide-medium w3-hide-small", "");
                }
                //play the Timeline, configured to play the small sidebar animation (Out) first and then at 0.3(30%), play the main sidebar animation (In)
                _Printer.Timelines['aim-printpdf-sidebar-timeline'].Play("aim-printpdf-small-menu-anim:0.3=>aim-printpdf-sidebar-anim");
            });

            //Assign function to the onclick event of the button(in the main sidebar) to show the small sidebar - display only in small and medium screen (using w3css)
            //the function animates out the main sidebar and animate in the small sidebar using the Timeline #printPdfTM
            _Assign('click', _('aim-printpdf-show-small-menu'), function () {
                // e.stopPropagation();
                var sidebarAnim = _Printer.Animations['aim-printpdf-sidebar'], smallSidebarAnim = _Printer.Animations['aim-printpdf-small-menu'];

                sidebarAnim.Direction = "fro";//set the main sidebar animation direction to reverse (move out)
                smallSidebarAnim.Direction = "to";//set the small sidebar animation direction to forward (move in)
                sidebarAnim.OnStart = function () { };//clear the OnStart Operation of the main sidebar (incase it is set from its previous animation)

                //set the OnEnd to add the w3 hide class on it and reset the effect of the animation (left and opacity) so that it will be hidden on small and medium screen but visible on large screen
                sidebarAnim.OnEnd = function () {
                    var cn = _('aim-printpdf-sidebar'); //get the sidebar using Aim finder
                    //add the w3css hide classes so as to return back the w3css hide effect on small and medium screen
                    cn.className = cn.className + " w3-hide-medium w3-hide-small";
                    //reset the effect of the animation, so as to make it visible on large screen
                    _Style(cn, "left:0px;opacity:1");
                }
                //play the Timeline, configured to play the main sidebar animation (Out) first and then at 0.3(30%), play the small sidebar animation (In)
                _Printer.Timelines['aim-printpdf-sidebar-timeline'].Play("aim-printpdf-sidebar-anim:0.3=>aim-printpdf-small-menu-anim");
            });
            /* _Assign('click',_('aim-printer-open'),function(e){
         // e.stopPropagation();
              _Printer.Load({PaperType:"Letter",Orientation:"L",FontSize:35,MarginTop:21,MarginBottom:12,MarginLeft:32,MarginRight:10,Src:"",Title:"Testing Aim Printer"});
          }) */
            _Assign('click', _('aim-printer-close'), function (e) {
                // e.stopPropagation();
                _Printer.Close();
                // _('aim-printpdf').ZoomOut();
            });

            //the download buttons
            _Assign('click', _('aim-printer-download-btn'), function (e) {
                // e.stopPropagation();
                _Printer.Download();
            });

            //Initialize the popup-close-button (aim-alert-close)
            //get the popup object
            /* var pobj = _('aim-alert-ind');
            if(_IsFound(pobj)){
              pobj = _IsArray(pobj)?pobj:[pobj];
              pobj.Each(function(ipobj){
                  ipobj = ipobj.nextElementSibling;
                  _Proccessor.Init(ipobj);
              }); */

            // pobj = _IsArray(pobj)?pobj[0]:pobj;
            //get the close button
            /* var clbtn = pobj._('aim-alert-close');
             if(_IsFound(clbtn)){
               
               clbtn = _IsArray(clbtn)?clbtn:[clbtn];
               clbtn.Each(function(cbtn){
                   
                   cbtn.Assign(function(e){
                       _.Log("Alert Button Clicked "+ e.currentTarget.className);
                       _('aim-popup-ind').nextElementSibling.style.display = 'none';
                   });
               })
             }*/
            /*  } */


            //***************************************** */
            /* }
            //printer initialization complete
            _InitManager.Complete("Printer"); */
            /*  },
             OnError:function(){
                 _.Log("Printer Markup loading Failed");
                 //printer initialization complete
               _InitManager.Complete("Printer");
             }
         });//Ajax post ends */
            //if not alraedy added if block end 
            /*  }else{
              _InitManager.Complete("Printer");
             } */

        }//Initialize the printer object Ends

    }

    //#Drag object
    _Drag = {
        CurrentElement: null, //hold the current element user for dragging ()
        Elements: [], //all registered element that is draggable
        ElementPos: [0, 0, 0, 0], //holds positions of the current element ()
        //Register elements,
        Param: [],//hold the drag element parameters
        CurrentParam: null,//hold the current drag element parameters
        Element: function (obj, param) {
            //obj => the element(s) to move when dragging (Array Allowed)
            //param
            //1. Handle => the element that user will click or drag to drag the obj.
            //NB: Handle is recormended to be inside obj
            //NB: if handle is not set the obj is used instead
            //2. MaximumX, MaximumY, MinimumX, MinimumY => use to set the maximum drag level of X and Y and the Minimum level of X and Y respectively
            //NB: if the mouse cursur moves beyond or below the limit level the drag operation will be terminated
            //3. Restricted: the confined drag area (usualy an element containing the drag element)
            //NB: if set as true will use the drag elment parentElement
            //NB: aim finder selector string is allowed
            //4. Direction: the direction of dragging ('Both'-default, X-move withen X axis only, Y-move withen Y axis only)
            //5. OnStart => event to trigger when drag starts (when mouse is press down for dragging). recieve ({Element:moving element, ClientX:curent mouse X location, ClientY:current Y mouse loation}) as argurment
            //6. OnDrag => event handler to be trigger durring drag. receive ({Element:elem,Top:elem css top,Left:elem css left})
            //7. OnFinsh => event handler to be trigger when drag ends (when mouse is released or drag is force to stop. receive ({Element:elem})


            //if know param set, create/set an empty object
            param = typeof param == _UND ? {} : param;


            //handle => the element to drag (recommended to be inside obj)
            //if handle not set use the obj 
            elmnt = _RP(obj);
            elmnt = _IsArray(elmnt) ? elmnt : [elmnt];

            //Set Default parameters
            handle = typeof param.Handle != _UND && param.Handle != null ? _RP(param.Handle) : elmnt;
            param.Direction = typeof param.Direction == _UND ? "Both" : param.Direction;
            param.MaximumX = typeof param.MaximumX == _UND ? null : param.MaximumX;
            param.MaximumY = typeof param.MaximumY == _UND ? null : param.MaximumY;
            param.MinimumX = typeof param.MinimumX == _UND ? null : param.MinimumX;
            param.MinimumY = typeof param.MinimumY == _UND ? null : param.MinimumY;

            //loop through all object to register for drag operation
            for (var index = 0; index < elmnt.length; index++) {
                var element = elmnt[index];
                //make the element absolute
                element.style.position = "absolute";
                //get the handle
                var chandle = _IsArray(handle) ? handle[index] : handle;
                //set the handle onmousedown method
                chandle.onmousedown = _Drag.DragMouseDown;
                //include the current element to the registered element array, using the handle element id as key
                _Drag.Elements[_GetId(chandle)] = element;
                //alert(param.Restricted);
                //check if Restricted is set and is true(parent Element) or an element
                //overide maximum/minimum X and Y to restrict draging to the the confined element
                //if restriction set
                if (typeof param.Restricted != _UND && param.Restricted != null && param.Restricted !== false) {
                    //check if restriction is set to true, meaning the drag parent element is to be used
                    if (param.Restricted === true) {
                        param.Restricted = element.parentElement;
                    } else {
                        //else try get the Restricted element
                        param.Restricted = _RP(param.Restricted);
                        //if restricted element found
                        if (_IsFound(param.Restricted)) {
                            //if restricted element found is an array i.e more than one
                            //use the first element as the restricted element
                            if (_IsArray(param.Restricted)) {
                                param.Restricted = param.Restricted[0];
                            }

                        } else { //if restricted element not found
                            //use the elements parent element
                            //param.Restricted = element.parentElement;
                        }

                    }
                    //get the user defined position of the Restrict element
                    var restpos = _Trim(param.Restricted.style.position);
                    //if cannot identify position (i.e not set or not set as inline), set it as relative
                    if (restpos == "") param.Restricted.style.position = restpos = "relative";
                    var pdim = _GetDimension(param.Restricted);
                    var elemdim = _GetDimension(element);
                    param.MaximumY = pdim.height - elemdim.height;
                    // param.MaximumY = param.Restricted.offsetTop + pdim.height - elemdim.height;
                    //param.MinimumY=param.Restricted.offsetTop;
                    param.MinimumY = 0;
                    // param.MaximumX=param.Restricted.offsetLeft + pdim.width - elemdim.width;
                    param.MaximumX = pdim.width - elemdim.width;
                    //param.MinimumX=param.Restricted.offsetLeft;
                    param.MinimumX = 0;

                    //make sure element is in its restricted region
                    // if(element.offsetTop < param.Restricted.offsetTop)element.style.top = param.Restricted.offsetTop + "px";
                    if (element.offsetTop < 0) element.style.top = "0px";
                    //if(element.offsetLeft < param.Restricted.offsetLeft)element.style.left = param.Restricted.offsetLeft + "px";
                    if (element.offsetLeft < 0) element.style.left = "0px";

                }

                //include the current element parameters to the registered element parameter array, using the handle element id as key
                _Drag.Param[_GetId(chandle)] = param;
                //set as current elements


            }

        },

        //The onmousedown event handler fro the handle
        DragMouseDown: function (e) {
            e = e || window.event;
            var cobj = e.currentTarget; //get the handle element calling this method
            //set the current drag element to the handle move elemnt from the registered elements array using the handle id as key
            _Drag.CurrentElement = _Drag.Elements[cobj.id];

            //set the current drag element parameter from the registered elements parameter array using the handle id as key
            _Drag.CurrentParam = _Drag.Param[cobj.id];

            // get the mouse cursor position at startup:
            //and keep in the third & forth position of ElemntsPos array for X and Y respectively
            _Drag.ElementPos[2] = e.clientX
            _Drag.ElementPos[3] = e.clientY
            if (typeof _Drag.CurrentParam.OnStart != _UND) _Drag.CurrentParam.OnStart({ Element: _Drag.CurrentElement, ClientX: e.clientX, ClientY: e.clientY });

            //set the document onmouseup event handler (Stop dragging)
            document.onmouseup = _Drag.CloseDragElement;


            // call a function whenever the cursor moves:
            document.onmousemove = _Drag.ElementDrag;

        },

        //the cursor/mouse move event handler
        ElementDrag: function (e) {
            e = e || window.event;

            //if no drag element and drag param set;
            if (_Drag.CurrentParam == null || _Drag.CurrentElement == null) {
                _Drag.CloseDragElement();
                return;
            }

            // calculate the new cursor position:
            //(Current Position - New Position)
            _Drag.ElementPos[0] = _Drag.ElementPos[2] - e.clientX;
            _Drag.ElementPos[1] = _Drag.ElementPos[3] - e.clientY;

            //reset the mouse cursur position to new position
            _Drag.ElementPos[2] = e.clientX;
            _Drag.ElementPos[3] = e.clientY;

            var ntop = 0, nleft = 0;
            // set the element's new position:
            if (_Drag.CurrentParam.Direction == 'Both' || _Drag.CurrentParam.Direction == 'Y') {
                //calculate the new value and set elements position
                var maxY = _Drag.CurrentParam.MaximumY,
                    minY = _Drag.CurrentParam.MinimumY,
                    ntop = _Drag.CurrentElement.offsetTop - _Drag.ElementPos[1];
                //disable dragging if max or min move level passed
                if ((maxY != null && ntop > maxY && e.pageY > maxY + _GetDimension(_Drag.CurrentElement).height) || (minY != null && ntop < minY && e.pageY < minY)) {
                    _Drag.CloseDragElement();
                }
                // _Drag.CurrentElement.innerHTML = "clientY: "+ e.pageY + " <br /> new Y: "+nval; 
                ntop = (maxY != null && ntop > maxY) ? maxY : ntop;
                ntop = (minY != null && ntop < minY) ? minY : ntop;
                if (_Drag.CurrentElement != null) _Drag.CurrentElement.style.top = ntop + "px";

            }

            if (_Drag.CurrentParam != null && (_Drag.CurrentParam.Direction == 'Both' || _Drag.CurrentParam.Direction == 'X')) {
                //calculate the new value and set elements position 
                var maxX = _Drag.CurrentParam.MaximumX,
                    minX = _Drag.CurrentParam.MinimumX,
                    nleft = _Drag.CurrentElement.offsetLeft - _Drag.ElementPos[0];
                //disable dragging if max or min move level passed
                if ((maxX != null && nleft > maxX && e.pageX > maxX + _GetDimension(_Drag.CurrentElement).width) || (minX != null && nleft < minX && e.pageX < minX)) {
                    _Drag.CloseDragElement();
                }
                nleft = (maxX != null && nleft > maxX) ? maxX : nleft;
                nleft = (minX != null && nleft < minX) ? minX : nleft;
                if (_Drag.CurrentElement != null) _Drag.CurrentElement.style.left = nleft + "px";
            }
            if (_Drag.CurrentElement != null) _Drag.CurrentParam.OnDrag({ Element: _Drag.CurrentElement, Top: ntop, Left: nleft, ClientX: e.clientX, ClientY: e.clientY });
            //_Drag.CurrentElement.innerHTML = "mcX: "+e.clientX+"<br />"+"mcY: "+e.clientY+"<br />"+"elemOffPosLeft: "+_Drag.CurrentElement.offsetLeft+"<br />"+"elemOffPosTop: "+_Drag.CurrentElement.offsetTop+"<br />"+"elemTop: "+_Drag.CurrentElement.style.top+"<br />"+"elemLeft: "+_Drag.CurrentElement.style.left+"<br />"
        },

        CloseDragElement: function () {
            /* stop moving when mouse button is released:*/
            document.onmouseup = null;
            document.onmousemove = null;
            if (typeof _Drag.CurrentParam.OnFinish != _UND) _Drag.CurrentParam.OnFinish({ Element: _Drag.CurrentElement });
            _Drag.CurrentParam = null; _Drag.CurrentElement = null;
        }

    }

    _Camera = {
        getUserMedia: function (options, successCallback, failureCallback) {
            var api = navigator.getUserMedia || navigator.webkitGetUserMedia ||
                navigator.mozGetUserMedia || navigator.msGetUserMedia;
            if (api) {
                return api.bind(navigator)(options, successCallback, failureCallback);
            }
        },

        theStream: {},
        Photos: {},

        getStream: function (videoid, fn, data) {
            // if(typeof fn == "function")fn(data);
            fn = fn || null;
            data = data || videoid;
            if (!navigator.getUserMedia && !navigator.webkitGetUserMedia &&
                !navigator.mozGetUserMedia && !navigator.msGetUserMedia) {
                alert('User Media API not supported.');
                return;
            }

            var constraints = {
                video: true
            };

            _Camera.getUserMedia(constraints, function (stream) {
                var mediaControl = document.querySelector('#' + videoid);
                if ('srcObject' in mediaControl) {
                    mediaControl.srcObject = stream;
                } else if (navigator.mozGetUserMedia) {
                    mediaControl.mozSrcObject = stream;
                } else {
                    mediaControl.src = (window.URL || window.webkitURL).createObjectURL(stream);
                }
                _Camera.theStream[videoid] = stream;
                if (typeof fn == "function") fn(data);
            }, function (err) {
                alert('Error: ' + err);
            });
        },

        takePhoto: function (videoid, imgph, fn, data) {
            fn = fn || null;
            data = data || videoid;
            if (!('ImageCapture' in window)) {
                alert('ImageCapture is not available');
                return;
            }

            if (!_Camera.theStream[videoid]) {
                alert('Grab the video stream first!');
                return;
            }

            var theImageCapturer = new ImageCapture(_Camera.theStream[videoid].getVideoTracks()[0]);

            theImageCapturer.takePhoto()
                .then(blob => {
                    var theImageTag = document.getElementById(imgph);
                    theImageTag.src = URL.createObjectURL(blob);
                    //add it to camera photos
                    _Camera.Photos[imgph + "_File"] = blob;
                    _Camera.SetPhoto(videoid, imgph + "_File");
                    if (typeof fn == "function") fn(data);
                })
                .catch(err => alert('Error: ' + err));
        },
        SetPhoto: (vid, fid) => {
            //get thevideo
            var video = _(vid); if (!_IsFound(video)) return;
            var vidobj = _(fid); if (_IsFound(vidobj)) { vidobj.parentElement.removeChild(vidobj) };

            var hidenelem = document.createElement('input');
            hidenelem.type = "hidden";
            hidenelem.id = fid;
            hidenelem.value = vid;
            hidenelem.className = "cameraPhoto";
            video.parentElement.insertBefore(hidenelem, video);
        }
    }


    //check if the screen is full or not
    _IsFullMode = function () {
        if (
            document.fullscreenElement ||
            document.webkitFullscreenElement ||
            document.mozFullScreenElement ||
            document.msFullscreenElement
        ) {
            return true;
        } else {
            return false;
        }
    }

    _FullMode = function () {
        // element = typeof page == _.UND?document.documentElement:page;
        element = document.documentElement;
        // Supports most browsers and their versions.
        var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullscreen;

        if (requestMethod) { // Native full screen.

            requestMethod.call(element);
        } else if (typeof window.ActiveXObject !== "undefined") { // Older IE.
            var wscript = new ActiveXObject("WScript.Shell");
            if (wscript !== null) {
                wscript.SendKeys("{F11}");
            }
        }
    }

    //Exit Fullscreen Mode
    _ExitFullMode = function () {
        if (document.exitFullscreen) {
            document.exitFullscreen();
        } else if (document.webkitExitFullscreen) {
            document.webkitExitFullscreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        }
    }
    //toggle fullscreen mode
    _ToggleFullMode = function () {
        if (_IsFullMode()) {
            _ExitFullMode();
        } else {
            _FullMode();
        }
    }

})();

/*
*convert number to word
*/
function toWord (numberInput){
    // let numberInput = document.querySelector('#numberInput').value ;
    // let myDiv = document.querySelector('#result');
     let numarray = numberInput.toString().split('.');
     numberInput = numarray[0];
     let point = numarray.length > 1?numarray[1]:'';
     
    let oneToTwenty = ['zero ','one ','two ','three ','four ', 'five ','six ','seven ','eight ','nine ','ten ',
    'eleven ','twelve ','thirteen ','fourteen ','fifteen ','sixteen ','seventeen ','eighteen ','nineteen '];
    let tenth = ['', '', 'twenty','thirty','forty','fifty', 'sixty','seventy','eighty','ninety'];
    let decimalpart = "";
if(point != "" && /^\d+$/.test(point)){ //if point exist
    if(Number(point) > 0){
       for (var i = 0; i < point.length; i++) {
        decimalpart += oneToTwenty[Number(point[i])];
      } 
    } 
}
    if(numberInput.toString().length > 7) return 'Over-limit' ;
    //console.log(numberInput);
    //let num = ('0000000000'+ numberInput).slice(-10).match(/^(\d{1})(\d{2})(\d{2})(\d{2})(\d{1})(\d{2})$/);
  let num = ('0000000'+ numberInput).slice(-7).match(/^(\d{1})(\d{1})(\d{2})(\d{1})(\d{2})$/);
    //console.log(num);
    if(!num) return;

    let outputText = num[1] != 0 ? (oneToTwenty[Number(num[1])] || `${tenth[num[1][0]]} ${oneToTwenty[num[1][1]]}` )+' million ' : ''; 
  
    outputText +=num[2] != 0 ? (oneToTwenty[Number(num[2])] || `${tenth[num[2][0]]} ${oneToTwenty[num[2][1]]}` )+'hundred ' : ''; 
    outputText +=num[3] != 0 ? (oneToTwenty[Number(num[3])] || `${tenth[num[3][0]]} ${oneToTwenty[num[3][1]]}`)+' thousand ' : ''; 
    outputText +=num[4] != 0 ? (oneToTwenty[Number(num[4])] || `${tenth[num[4][0]]} ${oneToTwenty[num[4][1]]}`) +'hundred ': ''; 
    outputText +=num[5] != 0 ? (oneToTwenty[Number(num[5])] || `${tenth[num[5][0]]} ${oneToTwenty[num[5][1]]} `) : ''; 
  return outputText + (decimalpart != ""?" point "+decimalpart : "");
    // myDiv.innerHTML = outputText;
}

/*
* onDOMReady
* Copyright (c) 2009 Ryan Morr (ryanmorr.com)
* Licensed under the MIT license.
*/
function onDOMReady(fn, ctx) {
    var ready, timer;
    var onStateChange = function (e) {
        if (e && e.type == "DOMContentLoaded") {
            fireDOMReady()
        } else if (e && e.type == "load") {
            fireDOMReady()
        } else if (document.readyState) {
            if ((/loaded|complete/).test(document.readyState)) {
                fireDOMReady()
            } else if (!!document.documentElement.doScroll) {
                try {
                    ready || document.documentElement.doScroll('left')
                } catch (e) {
                    return
                }
                fireDOMReady()
            }
        }
    };
    var fireDOMReady = function () {
        if (!ready) {
            ready = true;
            fn.call(ctx || window);
            if (document.removeEventListener) document.removeEventListener("DOMContentLoaded", onStateChange, false);
            document.onreadystatechange = null;
            window.onload = null;
            clearInterval(timer);
            timer = null
        }
    };
    if (document.addEventListener) document.addEventListener("DOMContentLoaded", onStateChange, false);
    document.onreadystatechange = onStateChange;
    timer = setInterval(onStateChange, 5);
    window.onload = onStateChange
};