/*
 * @project: TERA
 * @version: Development (beta)
 * @license: MIT (not for evil)
 * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com]
 * Web: https://terafoundation.org
 * Twitter: https://twitter.com/terafoundation
 * Telegram:  https://t.me/terafoundation
*/
function $(id)
{
    return document.getElementById(id);
};
window.Storage = {};
window.Storage.setItem = function (Key,Value)
{
    if(window.localStorage)
        localStorage.setItem(Key, Value);
};
window.Storage.getItem = function (Key)
{
    if(window.localStorage)
        return localStorage.getItem(Key);
};
var WALLET_KEY_NAME = "WALLET_KEY";
var WALLET_PUB_KEY_NAME = "WALLET_PUB_KEY";
if(!Math.log2)
    Math.log2 = Math.log2 || function (x)
    {
        return Math.log(x) * Math.LOG2E;
    };
if(!window.crypto)
    window.crypto = window.msCrypto;
if(!window.toStaticHTML)
    toStaticHTML = function (Str)
    {
        return Str;
    };
if(!String.prototype.padStart)
{
    window.BrowserIE = 1;
    String.prototype.padStart = function padStart(targetLength,padString)
    {
        targetLength = targetLength >> 0;
        padString = String((typeof padString !== 'undefined' ? padString : ' '));
        if(this.length > targetLength)
        {
            return String(this);
        }
        else
        {
            targetLength = targetLength - this.length;
            if(targetLength > padString.length)
            {
                padString += padString.repeat(targetLength / padString.length);
            }
            return padString.slice(0, targetLength) + String(this);
        }
    };
}
window.IsLocalClient = function ()
{
    return (window.location.protocol.substr(0, 4) !== "http");
};
var ServerHTTP;
var MainServer;
if(window.nw)
{
    window.Open = function (path,iconname,width,height)
    {
        width = width || 840;
        height = height || 1000;
        var params = {width:width, height:height};
        if(iconname)
            params.icon = "../HTML/PIC/" + iconname + ".png";
        window.nw.Window.open(path, params, function (win)
        {
        });
    };
    window.GetData = function (Method,ObjPost,Func)
    {
        window.nw.global.RunRPC({path:Method, obj:ObjPost}, Func);
    };
    global.RunRPC = function (message,Func)
    {
        if(!ServerHTTP)
            ServerHTTP = require('../core/html-server');
        var reply = ServerHTTP.SendData(message);
        if(Func)
        {
            Func(reply);
        }
    };
}
else
{
    window.Open = function (path,iconname,width,height)
    {
        if(!window.NWMODE)
        {
            var win = window.open(path);
        }
        else
        {
            width = width || 840;
            height = height || 1000;
            var left = (screen.width - width) / 2;
            var params = "left=" + left + ",top=24,menubar=no,location=no,resizable=yes,scrollbars=no,status=no";
            params += ",width=" + width;
            params += ",height=" + height;
            var win = window.open(path, undefined, params);
        }
    };
    window.GetData = function (Method,ObjPost,Func)
    {
        if(Method.substr(0, 4) !== "http")
        {
            if(Method.substr(0, 1) !== "/")
                Method = "/" + Method;
            if(MainServer)
            {
                Method = GetProtocolServerPath(MainServer) + Method;
            }
            else
            {
                if(IsLocalClient())
                    return ;
            }
        }
        var StrPost = null;
        var serv = new XMLHttpRequest();
        if(ObjPost !== null)
        {
            StrPost = JSON.stringify(ObjPost);
            serv.open("POST", Method, true);
        }
        else
        {
            throw "ERROR GET-TYPE";
        }
        var STACK = "" + new Error().stack;
        serv.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        serv.onreadystatechange = function ()
        {
            if(serv.readyState == 4)
            {
                if(serv.status == 200)
                {
                    if(Func)
                    {
                        var Data;
                        try
                        {
                            Data = JSON.parse(serv.responseText);
                        }
                        catch(e)
                        {
                            console.log("Error parsing: " + e);
                            console.log(serv.responseText);
                            console.log(STACK);
                        }
                        Func(Data, serv.responseText);
                    }
                }
                else
                {
                    if(Func)
                        Func(undefined, undefined);
                }
            }
        };
        serv.send(StrPost);
    };
}
function IsIPAddres(Str)
{
    var arr = Str.split(".");
    if(arr.length !== 3)
        return 0;
    for(var i = 0; i < arr.length; i++)
        if(arr[i] !== "" + ParseNum(arr[i]))
            return 0;
    return 1;
};
function GetProtocolServerPath(Item)
{
    if(Item.port === 443)
        return "https://" + Item.ip;
    else
        if(Item.port === 80)
            return "http://" + Item.ip;
        else
            return "http://" + Item.ip + ":" + Item.port;
};
function SUM_TO_STRING(Value,Currency,bFloat,bLocal)
{
    var Str;
    if(Value.SumCOIN || Value.SumCENT)
        if(bFloat)
        {
            Str = "" + FLOAT_FROM_COIN(Value).toStringF();
        }
        else
        {
            var SumCOIN = Value.SumCOIN;
            if(bLocal)
                SumCOIN = SumCOIN.toLocaleString();
            Str = "" + SumCOIN + "." + Rigth("000000000" + Value.SumCENT, 9);
        }
    else
        Str = "";
    if(Currency !== undefined)
    {
        if(Str === "")
            Str = "0";
        Str += " " + CurrencyName(Currency);
    }
    return Str;
};
function GetArrFromHex(Str)
{
    var array = [];
    for(var i = 0; Str && i < Str.length / 2; i++)
    {
        array[i] = parseInt(Str.substr(i * 2, 2), 16);
    }
    return array;
};
function GetHexFromArr(arr)
{
    if(!(arr instanceof Array) && arr.data)
        arr = arr.data;
    var Str = "";
    for(var i = 0; arr && i < arr.length; i++)
    {
        if(!arr[i])
            Str += "00";
        else
        {
            var Val = arr[i] & 255;
            var A = Val.toString(16);
            if(A.length === 1)
                A = "0" + A;
            Str = Str + A;
        }
    }
    return Str.toUpperCase();
};
function GetStrFromAddr(arr)
{
    return GetHexFromArr(arr);
};
function GetHexFromArrBlock(Arr,LenBlock)
{
    var Str = "";
    var Arr2 = [];
    for(var i = 0; i < Arr.length; i++)
    {
        Arr2[i % LenBlock] = Arr[i];
        if(Arr2.length >= LenBlock)
        {
            Str += GetHexFromArr(Arr2) + "\n";
            Arr2 = [];
        }
    }
    if(Arr2.length)
    {
        Str += GetHexFromArr(Arr2);
    }
    return Str;
};
function Rigth(Str,Count)
{
    if(Str.length < Count)
        return Str;
    else
        return Str.substr(Str.length - Count);
};
function toUTF8Array(str)
{
    var utf8 = [];
    for(var i = 0; i < str.length; i++)
    {
        var charcode = str.charCodeAt(i);
        if(charcode < 0x80)
            utf8.push(charcode);
        else
            if(charcode < 0x800)
            {
                utf8.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f));
            }
            else
                if(charcode < 0xd800 || charcode >= 0xe000)
                {
                    utf8.push(0xe0 | (charcode >> 12), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f));
                }
                else
                {
                    i++;
                    charcode = 0x10000 + (((charcode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
                    utf8.push(0xf0 | (charcode >> 18), 0x80 | ((charcode >> 12) & 0x3f), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f));
                }
    }
    return utf8;
};
function Utf8ArrayToStr(array)
{
    var out, i, len, c;
    var char2, char3;
    out = "";
    len = array.length;
    i = 0;
    while(i < len)
    {
        c = array[i++];
        switch(c >> 4)
        {
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
            case 6:
            case 7:
                out += String.fromCharCode(c);
                break;
            case 12:
            case 13:
                char2 = array[i++];
                out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
                break;
            case 14:
                char2 = array[i++];
                char3 = array[i++];
                out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
                break;
        }
    }
    for(var i = 0; i < out.length; i++)
    {
        if(out.charCodeAt(i) === 0)
        {
            out = out.substr(0, i);
            break;
        }
    }
    return out;
};
function GetArr32FromStr(Str)
{
    return GetArrFromStr(Str, 32);
};
function GetArrFromStr(Str,Len)
{
    var arr = toUTF8Array(Str);
    for(var i = arr.length; i < Len; i++)
    {
        arr[i] = 0;
    }
    return arr.slice(0, Len);
};
function WriteByte(arr,Num)
{
    arr[arr.length] = Num & 0xFF;
};
function WriteUint(arr,Num)
{
    var len = arr.length;
    arr[len] = Num & 0xFF;
    arr[len + 1] = (Num >>> 8) & 0xFF;
    arr[len + 2] = (Num >>> 16) & 0xFF;
    arr[len + 3] = (Num >>> 24) & 0xFF;
    var NumH = Math.floor(Num / 4294967296);
    arr[len + 4] = NumH & 0xFF;
    arr[len + 5] = (NumH >>> 8) & 0xFF;
};
function WriteUint16(arr,Num)
{
    var len = arr.length;
    arr[len] = Num & 0xFF;
    arr[len + 1] = (Num >>> 8) & 0xFF;
};
function WriteUint32(arr,Num)
{
    var len = arr.length;
    arr[len] = Num & 0xFF;
    arr[len + 1] = (Num >>> 8) & 0xFF;
    arr[len + 2] = (Num >>> 16) & 0xFF;
    arr[len + 3] = (Num >>> 24) & 0xFF;
};
function WriteStr(arr,Str,ConstLength)
{
    if(!Str)
        Str = "";
    var arrStr = toUTF8Array(Str);
    var length;
    var len = arr.length;
    if(ConstLength)
    {
        length = ConstLength;
    }
    else
    {
        length = arrStr.length;
        if(length > 65535)
            length = 65535;
        arr[len] = length & 0xFF;
        arr[len + 1] = (length >>> 8) & 0xFF;
        len += 2;
    }
    for(var i = 0; i < length; i++)
    {
        arr[len + i] = arrStr[i];
    }
};
function WriteArr(arr,arr2,ConstLength)
{
    var len = arr.length;
    for(var i = 0; i < ConstLength; i++)
    {
        arr[len + i] = arr2[i];
    }
};
function WriteTr(arr,arr2)
{
    var len2 = arr2.length;
    var len = arr.length;
    arr[len] = len2 & 0xFF;
    arr[len + 1] = (len2 >>> 8) & 0xFF;
    len += 2;
    for(var i = 0; i < len2; i++)
    {
        arr[len + i] = arr2[i];
    }
};
function ReadUintFromArr(arr,len)
{
    if(len === undefined)
    {
        len = arr.len;
        arr.len += 6;
    }
    var value = (arr[len + 5] << 23) * 2 + (arr[len + 4] << 16) + (arr[len + 3] << 8) + arr[len + 2];
    value = value * 256 + arr[len + 1];
    value = value * 256 + arr[len];
    return value;
};
function ReadUint32FromArr(arr,len)
{
    if(len === undefined)
    {
        len = arr.len;
        arr.len += 4;
    }
    var value = (arr[len + 3] << 23) * 2 + (arr[len + 3] << 16) + (arr[len + 1] << 8) + arr[len];
    return value;
};
function ReadArr(arr,length)
{
    var Ret = [];
    var len = arr.len;
    for(var i = 0; i < length; i++)
    {
        Ret[i] = arr[len + i];
    }
    arr.len += length;
    return Ret;
};
function ReadStr(arr)
{
    var length = arr[arr.len] + arr[arr.len + 1] * 256;
    arr.len += 2;
    var arr2 = arr.slice(arr.len, arr.len + length);
    var Str = Utf8ArrayToStr(arr2);
    arr.len += length;
    return Str;
};
function ParseNum(Str)
{
    var Res = parseInt(Str);
    if(isNaN(Res))
        Res = 0;
    if(!Res)
        Res = 0;
    if(Res < 0)
        Res = 0;
    return Res;
};
function parseUint(Str)
{
    var Res = parseInt(Str);
    if(isNaN(Res))
        Res = 0;
    if(!Res)
        Res = 0;
    if(Res < 0)
        Res = 0;
    return Res;
};
function CopyObjKeys(dest,src)
{
    for(var key in src)
    {
        dest[key] = src[key];
    }
};
function SaveToArr(Arr,Obj)
{
    for(var key in Obj)
    {
        Arr[0]++;
        var Value = Obj[key];
        switch(typeof Value)
        {
            case "number":
                WriteByte(Arr, 241);
                WriteUint(Arr, Value);
                break;
            case "string":
                WriteByte(Arr, 242);
                WriteStr(Arr, Value);
                break;
            case "object":
                if(Value && (Value.length > 0 || Value.length === 0) && Value.length <= 240)
                {
                    WriteByte(Arr, Value.length);
                    WriteArr(Arr, Value, Value.length);
                    break;
                }
            default:
                WriteByte(Arr, 250);
        }
    }
};
function LoadFromArr(Arr,Obj)
{
    if(!Arr.length)
        return false;
    var Count = Arr[0];
    Arr.len = 1;
    for(var key in Obj)
    {
        if(!Count)
            break;
        Count--;
        var Type = Arr[Arr.len];
        Arr.len++;
        switch(Type)
        {
            case 241:
                Obj[key] = ReadUintFromArr(Arr);
                break;
            case 242:
                Obj[key] = ReadStr(Arr);
                break;
            default:
                if(Type <= 240)
                {
                    var length = Type;
                    Obj[key] = ReadArr(Arr, length);
                    break;
                }
                else
                {
                    Obj[key] = undefined;
                }
        }
    }
    if(Arr[0])
        return true;
    else
        return false;
};
var entityMap = {"&":"&", "<":"<", ">":">", '"':'"', "'":''', "/":'/', "\n":'
', " ":' ', };
function escapeHtml(string)
{
    string = string.replace(/\\n/g, "\n");
    string = string.replace(/\\"/g, "\"");
    return String(string).replace(/[\s\n&<>"'\/]/g, function (s)
    {
        return entityMap[s];
    });
};
function InsertAfter(elem,refElem)
{
    var parent = refElem.parentNode;
    var next = refElem.nextSibling;
    if(next)
    {
        return parent.insertBefore(elem, next);
    }
    else
    {
        return parent.appendChild(elem);
    }
};
function MoveUp(elem)
{
    var parent = elem.parentNode;
    for(var i = 0; i < parent.children.length; i++)
    {
        var item = parent.children[i];
        if(item.id && item.id !== undefined)
        {
            return parent.insertBefore(elem, item);
        }
    }
};
function ViewGrid(APIName,Params,nameid,bClear,TotalSum)
{
    GetData(APIName, Params, function (Data)
    {
        if(!Data || !Data.result)
            return ;
        SetGridData(Data.arr, nameid, TotalSum, bClear);
    });
};
function CheckNewSearch(Def)
{
    var Str = $(Def.FilterName).value;
    if(Str)
    {
        $(Def.NumName).value = "0";
    }
};
function ViewCurrent(Def,flag,This)
{
    if(Def.BlockName)
    {
        var element = $(Def.BlockName);
        if(flag)
        {
            var bVisible = IsVisibleBlock(Def.BlockName);
            if(!bVisible)
                MoveUp(element);
            SetVisibleBlock(Def.BlockName, !bVisible);
        }
        else
        {
            SetVisibleBlock(Def.BlockName, true);
        }
        var ResVisible = IsVisibleBlock(Def.BlockName);
        if(This && This.className)
        {
            This.className = This.className.replace("btpress", "");
            if(ResVisible)
                This.className += " btpress";
        }
        if(!ResVisible)
            return ;
    }
    var item = $(Def.NumName);
    var Filter = "", Filter2 = "";
    if(Def.FilterName)
    {
        Filter = $(Def.FilterName).value;
    }
    if(Def.FilterName2)
    {
        Filter2 = $(Def.FilterName2).value;
    }
    if(!Def.Param3)
        Def.Param3 = "";
    ViewGrid(Def.APIName, {StartNum:ParseNum(item.value), CountNum:GetCountViewRows(Def), Param3:Def.Param3, Filter:Filter, Filter2:Filter2,
    }, Def.TabName, 1, Def.TotalSum);
    SaveValues();
    if(This)
        SetImg(This, Def.BlockName);
};
function ViewPrev(Def)
{
    var item = document.getElementById(Def.NumName);
    var Num = ParseNum(item.value);
    Num -= GetCountViewRows(Def);
    if(Num < 0)
        Num = 0;
    item.value = Num;
    ViewCurrent(Def);
};
function ViewNext(Def,MaxNum)
{
    var item = document.getElementById(Def.NumName);
    var Num = ParseNum(item.value);
    Num += GetCountViewRows(Def);
    if(Def.FilterName)
    {
        if(document.getElementById(Def.FilterName).value)
        {
            Num = document.getElementById(Def.TabName).MaxNum + 1;
        }
    }
    if(Num < MaxNum)
    {
        item.value = Num;
    }
    else
    {
        item.value = MaxNum - MaxNum % GetCountViewRows(Def);
    }
    ViewCurrent(Def);
};
function ViewBegin(Def)
{
    document.getElementById(Def.NumName).value = 0;
    ViewCurrent(Def);
};
function ViewEnd(Def,MaxNum,bInitOnly)
{
    document.getElementById(Def.NumName).value = MaxNum - MaxNum % GetCountViewRows(Def);
    if(bInitOnly)
        return ;
    ViewCurrent(Def);
};
function GetCountViewRows(Def)
{
    if(Def.CountViewRows)
        return Def.CountViewRows;
    else
        return CountViewRows;
};
function DoStableScroll()
{
    var item = $("idStableScroll");
    if(!item)
        return ;
    var scrollHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight,
    document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight);
    var itemlHeight = Math.max(item.scrollHeight, item.offsetHeight, item.clientHeight);
    scrollHeight = scrollHeight - itemlHeight;
    item.style.top = "" + scrollHeight + "px";
};
var glEvalMap = {};
function CreateEval(formula,StrParams)
{
    var Ret = glEvalMap[formula];
    if(!Ret)
    {
        eval("function M(" + StrParams + "){return " + formula + "}; Ret=M;");
        glEvalMap[formula] = Ret;
    }
    return Ret;
};
var glWorkNum = 0;
var CUR_ROW;
function SetGridData(arr,id_name,TotalSum,bclear,revert)
{
    var htmlTable = document.getElementById(id_name);
    if(!htmlTable)
    {
        console.log("Error id_name: " + id_name);
        return ;
    }
    if(bclear)
        ClearTable(htmlTable);
    if(!htmlTable.ItemsMap)
    {
        htmlTable.ItemsMap = {};
        htmlTable.RowCount = 0;
    }
    var map = htmlTable.ItemsMap;
    glWorkNum++;
    var ValueTotal = {SumCOIN:0, SumCENT:0};
    var row0 = htmlTable.rows[0];
    var row0cells = row0.cells;
    var colcount = row0cells.length;
    for(var i = 0; arr && i < arr.length; i++)
    {
        var Item = arr[i];
        var ID = Item.Num;
        htmlTable.MaxNum = Item.Num;
        var row = map[ID];
        if(!row)
        {
            htmlTable.RowCount++;
            if(revert)
                row = htmlTable.insertRow(1);
            else
                row = htmlTable.insertRow( - 1);
            map[ID] = row;
            for(var n = 0; n < colcount; n++)
            {
                var cell0 = row0cells[n];
                if(cell0.innerText == "")
                    continue;
                cell0.F = CreateEval(cell0.id, "Item");
                if(cell0.id.substr(0, 1) === "(")
                    cell0.H = 1;
                var cell = row.insertCell(n);
                cell.className = cell0.className;
            }
        }
        row.Work = glWorkNum;
        CUR_ROW = row;
        for(var n = 0; n < colcount; n++)
        {
            var cell = row.cells[n];
            if(!cell)
                continue;
            var cell0 = row0cells[n];
            if(cell0.H)
            {
                var text = "" + cell0.F(Item);
                text = toStaticHTML(text.trim());
                if(cell.innerHTML !== text)
                    cell.innerHTML = text;
            }
            else
            {
                var text = "" + cell0.F(Item);
                text.trim();
                if(cell.innerText !== text)
                    cell.innerText = text;
            }
        }
        if(TotalSum && Item.Currency === 0)
            ADD(ValueTotal, Item.Value);
    }
    for(var key in map)
    {
        var row = map[key];
        if(row.Work !== glWorkNum)
        {
            htmlTable.deleteRow(row.rowIndex);
            delete map[key];
        }
    }
    if(TotalSum)
    {
        var id = document.getElementById(TotalSum);
        if(id)
        {
            if(!ISZERO(ValueTotal))
                id.innerText = "Total on page: " + SUM_TO_STRING(ValueTotal, 0, 0, 1);
            else
                id.innerText = "";
        }
    }
    DoStableScroll();
};
function ClearTable(htmlTable)
{
    for(var i = htmlTable.rows.length - 1; i > 0; i--)
        htmlTable.deleteRow(i);
    htmlTable.ItemsMap = {};
    htmlTable.RowCount = 0;
};
function RetOpenBlock(BlockNum,bTrDataLen)
{
    if(BlockNum && bTrDataLen)
    {
        if(bTrDataLen === 2)
        {
            return '' + BlockNum + '';
        }
        else
        {
            return '';
        }
    }
    else
        return '' + BlockNum + '';
};
function RetBool(Value)
{
    if(Value)
        return "✔";
    else
        return "";
};
function RetNumDapp(Item)
{
    return Item.Num;
};
function RetIconPath(Item,bCurrency)
{
    if(bCurrency && MapCurrencyIcon[Item.Num])
    {
        return MapCurrencyIcon[Item.Num];
    }
    var StrPath = "";
    if(MainServer)
    {
        StrPath = GetProtocolServerPath(MainServer);
    }
    if(Item.IconBlockNum)
    {
        return StrPath + '/file/' + Item.IconBlockNum + '/' + Item.IconTrNum;
    }
    else
        return StrPath + "/PIC/blank.svg";
};
function RetIconDapp(Item)
{
    if(Item.IconBlockNum)
    {
        return ' ';
    }
    else
        return "";
};
function RetOpenDapps(Item,bNum,AccountNum)
{
    var Name = escapeHtml(Item.Name);
    if(bNum)
        Name = "" + Item.Num + "." + Name;
    if(Item.HTMLLength > 0)
    {
        var StrText = RetIconDapp(Item) + Name;
        return '';
    }
    else
        return RetIconDapp(Item) + Name;
};
function RetDirect(Value)
{
    if(Value === "-")
    {
        return "-";
    }
    else
        if(Value === "+")
        {
            return "+";
        }
        else
            return "";
};
function RetCategory(Item)
{
    var Str = "";
    var Num = 0;
    if(Item.Category1 && MapCategory[Item.Category1])
    {
        Num++;
        Str += "" + Num + "." + MapCategory[Item.Category1] + "
";
    }
    if(Item.Category2 && MapCategory[Item.Category2])
    {
        Num++;
        Str += "" + Num + "." + MapCategory[Item.Category2] + "
";
    }
    if(Item.Category3 && MapCategory[Item.Category3])
    {
        Num++;
        Str += "" + Num + "." + MapCategory[Item.Category3] + "
";
    }
    Str = Str.substr(0, Str.length - 4);
    return Str;
};
function RetChangeSmart(Item)
{
    var Name = "";
    var State = "";
    var bOpen = 0;
    if(Item.SmartObj)
    {
        if(Item.SmartObj.HTMLLength)
        {
            Name = RetOpenDapps(Item.SmartObj, 1, Item.Num);
            bOpen = 1;
        }
        else
            Name = "" + Item.SmartObj.Num + "." + escapeHtml(Item.SmartObj.Name) + "
";
        if(window.DEBUG_WALLET)
            State = "
State:" + JSON.stringify(Item.SmartState);
    }
    var Height = 20;
    if(bOpen)
        Height = 40;
    return '