/* Minification failed. Returning unminified contents.
(1085,18-19): run-time error JS1010: Expected identifier: [
(1085,31-33): run-time error JS1004: Expected ';': of
(1085,34-42): run-time error JS1006: Expected ')': formData
(1085,52-53): run-time error JS1004: Expected ';': )
(2036,21-22): run-time error JS1195: Expected expression: >
(2054,14-15): run-time error JS1195: Expected expression: ,
(2055,20-21): run-time error JS1195: Expected expression: >
(2059,13-14): run-time error JS1002: Syntax error: }
(2066,5-6): run-time error JS1002: Syntax error: }
(2068,33-34): run-time error JS1195: Expected expression: )
(2068,35-36): run-time error JS1004: Expected ';': {
(2071,9-45): run-time error JS1018: 'return' statement outside of function: return gscreenshot.storage.lastChild
(2070,43-54): run-time error JS1018: 'return' statement outside of function: return null
 */
"use strict";
try { console.log("--> head.js 8.93"); } catch (e) { ; }

var ghead = ghead || {};
ghead.wasHistoryBackForward = false;
ghead.isSafari = false;

var App = App || {};
window.showDebugInfo = true;

String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g, ""); };
String.prototype.ltrim = function () { return this.replace(/^\s+/, ""); };
String.prototype.rtrim = function () { return this.replace(/\s+$/, ""); };
String.prototype.toInteger = function (def, min) { return toInteger(this, def, min); };
String.prototype.toFloat = function (def, min) { return toFloat(this, def, min); };
String.prototype.toBool = function (def) { return toBool(this, def); };
String.prototype.delLeftChar = function (char) {
    //deletes ALL [char] symbols from left side of the string
    var _s = this.trim();
    char = char.trim();
    if ("" === _s) return "";
    if ("" === char) return _s;
    var n = _s.length;
    for (var i = 0; i < n; i++) {
        if (1 > _s.length) return "";
        if (_s.left(1) === char) {
            if (1 === _s.length) return "";
            else _s = _s.right(_s.length - 1);
        }
        else return _s;
    }
    return _s;
};
String.prototype.delRightChar = function (char) {
    //deletes ALL [char] symbols from right side of the string
    var _s = this.trim();
    char = char.trim();
    if ("" === _s) return "";
    if ("" === char) return _s;
    var n = _s.length;
    for (var i = 0; i < n; i++) {
        if (1 > _s.length) return "";
        if (_s.right(1) === char) {
            if (1 >= _s.length) return "";
            else _s = _s.left(_s.length - 1);
        }
        else return _s;
    }
    return _s;
};
String.prototype.left = function (n, addStr) {
    // cuts string from the right side & adds [addStr]
    // sample: tg.left('1234567890',4,'...') => '1234...'
    if ("undefined" === typeof this || null === this || "undefined" === typeof (n) || null === n || 1 > n) return "";
    if ("undefined" === typeof addStr || null === addStr) addStr = "";
    if (n >= this.length) return this; return this.substr(0, n) + addStr;
};
String.prototype.right = function (n, addStr) {
    // cuts string from the left side & prepands [addStr]
    // sample: tg.left('1234567890',4,'...') => '...7890'
    if ("undefined" === typeof this || null === this || "undefined" === typeof n || null === n || 1 > n) return "";
    if ("undefined" === typeof addStr || null === addStr) addStr = "";
    if (n >= this.length) return this; return addStr + this.substr(this.length - n);
};
String.prototype.append = function (add, separator, label) {
    // if(source str is Null/Empty) => [label] + [add]
    // else => [source str] + [separator] + [label] + [add]
    // used to concatenate string like:
    // => a, b, c, d,...
    // => label1:a, label2:b, label3:c, label4:d,...
    
    if ("undefined" === typeof add || add === null) add = "";
    if (this === null) return add;
    var s = this.trim();
    if (s === "") return add;
    if (add === "") return s;
    if ("undefined" === typeof separator || separator === null) separator = "";
    if ("undefined" === typeof label || label === null) label = "";
    return s + separator + label + add;
};
String.prototype.getHTMLTag = function(tag) {
    if ("undefined" === typeof this || null === this) return "";
    if ("undefined" === typeof tag || null === tag) return this;
    var s = this.trim();
    if (s === "") return "";
    tag = tag.toLowerCase();
    switch (tag) {
        // tags without close
        case "br":
        case "link":
        case "meta":
            return "";
    }
    var x = s.indexOf("<" + tag + " ");
    if (x === -1) x = s.indexOf("<" + tag + ">");
    if (x === -1) x = s.indexOf("<" + tag.toUpperCase() + " ");
    if (x === -1) x = s.indexOf("<" + tag.toUpperCase() + ">");
    if (x === -1) return "";

    var y = s.indexOf("/" + tag + ">");
    if (y === -1) y = s.indexOf("/" + tag.toUpperCase() + ">");
    if (y === -1) y = s.length - 1;
    else y = y + tag.length + 2;
    if (y > s.length - 1) y = s.length - 1;

    s = s.substring(x, y);

    return s;
};
String.prototype.removeHTMLTags = function (tag, max, n) {

    //console.log(".removeHTMLTags() tag: " + tag + " n: " + n);

    if ("undefined" === typeof max || null === max || 1 > max) max = 1000000;
    if ("undefined" === typeof n || null === n) n = 1; else { n = parseInt(n, 0); if (isNaN(n)) n = 1; }
    if (n > max) return; // max iterations (max tags to remove)
    if ("undefined" === typeof this || null === this) return "";
    if ("undefined" === typeof tag || null === tag) return this;
    var s = this.trim();
    if (s === "") return "";
    tag = tag.toLowerCase();

    var x = s.indexOf("<" + tag + " ");
    if (x === -1) x = s.indexOf("<" + tag + ">");
    if (x === -1) x = s.indexOf("<" + tag.toUpperCase() + " ");
    if (x === -1) x = s.indexOf("<" + tag.toUpperCase() + ">");
    if (x === -1) return s;

    var y = -1;
    switch (tag) {
        // tags without close
        case "br":
        case "link":
        case "meta":
            y = s.indexOf(">", x);
            if (y > -1) y = y + 1;
            break;

        default:
            y = s.indexOf("/" + tag + ">");
            if (y === -1) y = s.indexOf("/" + tag.toUpperCase() + ">");
            if (y === -1) y = s.length - 1;
            else y = y + tag.length + 2;
    }

    if (y > s.length - 1) y = s.length - 1;

    //console.log(".removeHTMLTags() 200: x:" + x + " y: " + y);

    var result = "";
    if (x > 0) result = s.substring(0, x);
    if (y < s.length - 1) result += s.substring(y);

    //console.log(".removeHTMLTags() 300: result: " + result);

    // check if exist smore tags

    x = result.indexOf("<" + tag + " ");
    if (x === -1) x = result.indexOf("<" + tag + ">");
    if (x === -1) x = result.indexOf("<" + tag.toUpperCase() + " ");
    if (x === -1) x = result.indexOf("<" + tag.toUpperCase() + ">");

    //console.log(".removeHTMLTags() 320: x:" + x);

    if (x > -1) {
        //console.log(".removeHTMLTags() 900: ------- result: ");
        //console.log(result);

        return result.removeHTMLTags(tag, max, n + 1);
    }

    return result;
};
String.prototype.replaceHTMLTag = function (tag, newTag, newAttributes) {
    // search & replace HTML <[tag] att='1'>...</[tag]> to <[newTag] att='1'>...</[newTag]>;
    // if provided [newAttributes] -> replace also all attributes to new [newAttributes] (string : " att1='sss' att2='qqq'")
    var s = this.trim();
    if (s === "") return "";
    if ("undefined" === typeof newAttributes || null === newAttributes) newAttributes = "";
    var re = RegExp("<\\s*(" + tag + ")(\\s+[^>]*|\\s*)>", "ig");
    s = s.replace(re, "<" + newTag + (newAttributes !== "" ? " " + newAttributes : " $2") + " data-oldtag='$1'>");
    re = RegExp("<\\s*/\\s*" + tag + "\\s*>", "ig");
    s = s.replace(re, "</" + newTag + ">");
    return s;
};

Date.prototype.addDays = function (days) {
    var dat = new Date(this.valueOf());
    var hour1 = dat.getHours();
    dat.setTime(dat.getTime() + days * 86400000); // 24*60*60*1000 = 24 hours
    var hour2 = dat.getHours();
    // summertime occured +/- a WHOLE number of hours
    if (hour1 !== hour2) dat.setTime(dat.getTime() + (hour1 - hour2) * 3600000); // 60*60*1000 = 1 hour
    return dat;
};
Date.prototype.addYears = function (years) { return new Date(this.valueOf()).setYear(dat.getYear() + years); };

if (!Array.isArray) {
    Array.isArray = function (arg) {
        if ("undefined" === typeof arg || null === arg) return false;
        return Object.prototype.toString.call(arg) === '[object Array]';
    };
}

var enumBuildStatus = {
    NOT_CHECKED: 0,
    PROCESSING: 2,
    FINISHED: 10, //Completed or Failed
    FAILED: 11,
    CANCELLED: 12,
    OTHER: 100,
    toStr: function (v) {
        if ("string" === typeof v) v = this.toEnum(v);
        else if ("number" !== typeof v || null === v) v = 0;
        switch (v) {
            case 2: return "PROCESSING";
            case 10: return "FINISHED";
            case 11: return "FAILED";
            case 12: return "CANCELLED";
            case 100: return "OTHER";
            default: return "NOT_CHECKED";
        }
    },
    toLabel: function (v) {
        if ("string" === typeof v) v = this.toEnum(v);
        else if ("number" !== typeof v || null === v) v = 0;
        switch (v) {
            case 2: return "PROCESSING";
            case 10: return "COMPLETED";
            case 11: return "FAILED";
            case 12: return "CANCELLED";
            case 100: return "OTHER";
            default: return "NOT_CHECKED";
        }
    }
};

var enumAlertMsgType = {
    ERROR: "alert-danger",
    WARNING: "alert-warning",
    SUCCESS: "alert-success",
    INFO: "alert-info",
    UNDEFINED: "alert-warning",
    validate: function (value) {
        if ("undefined" === typeof value || null === value) return this.UNDEFINED;
        value = value.toString().trim().toLowerCase();
        switch (value) {
            case "error": case this.ERROR: return this.ERROR;
            case "warning": case this.WARNING: return this.WARNING;
            case "success": case this.SUCCESS: return this.SUCCESS;
            case "info": case this.INFO: return this.INFO;
            default: return this.UNDEFINED;
        }
    },
    getAll: function () {
        var arr = new Array();
        arr.push(this.ERROR);
        arr.push(this.WARNING);
        arr.push(this.SUCCESS);
        arr.push(this.INFO);
        arr.push(this.UNDEFINED);
        return arr;
    }
};

var enumPlatform = {
    None: 0,
    Android: 1,
    iOS: 2,
    Web: 3,
    Unity: 4,
    Desktop: 5,
    toStr: function (v) {
        if ("string" === typeof v) v = this.toEnum(v);
        else if ("number" !== typeof v || null === v) v = 0;
        switch (v) {
            case 1: return "android";
            case 2: return "ios";
            case 3: return "web";
            case 4: return "unity";
            case 5: return "desktop";
            default: return "none";
        }
    },
    toEnum: function (v) {
        if ("string" !== typeof v && "number" !== typeof v || null === v) v = "none";
        switch (v.toString().toLowerCase()) {
            case "1": case "android": return 1;
            case "2": case "ios": return 2;
            case "3": case "web": return 3;
            case "4": case "unity": return 4;
            case "5": case "desktop": return 5;
            default: return 0;
        }
    },
    toLabel: function (v) {
        if ("string" === typeof v) v = this.toEnum(v);
        else if ("number" !== typeof v || null === v) v = 0;
        switch (v) {
            case 1: return "Android";
            case 2: return "iOS";
            case 3: return "Web";
            case 4: return "Unity";
            case 5: return "Desktop";
            default: return "None";
        }
    },
};

var enumTemplateBlockState = {
    None: 0,
    WithSourceCodeOnly: 1,
    WithNoSourceCodeOnly: 2,
    WithAndWithoutSourceCode: 3,
    toStr: function (v) {
        if ("string" === typeof v) v = this.toEnum(v);
        else if ("number" !== typeof v || null === v) v = 0;
        switch (v) {
            case 1: return "WithSourceCodeOnly";
            case 2: return "WithNoSourceCodeOnly";
            case 3: return "WithAndWithoutSourceCode";
            default: return "None";
        }
    },
    toEnum: function (v) {
        if ("string" !== typeof v && "number" !== typeof v || null === v) v = "none";
        switch (v.toString().toLowerCase()) {
            case "1": case "withsourcecodeonly": return 1;
            case "2": case "withnosourcecodeonly": return 2;
            case "3": case "withandwithoutsourcecode": return 3;
            default: return 0;
        }
    }
};

var enumBlockType = {
    None: 0,
    NoSourceCode: 10,
    WithSourceCode: 20,
    toStr: function (v) {
        if ("string" === typeof v) v = this.toEnum(v);
        else if ("number" !== typeof v || null === v) v = 0;
        switch (v) {
            case 10: return "NoSourceCode";
            case 20: return "WithSourceCode";
            default: return "None";
        }
    },
    toEnum: function (v) {
        if ("string" !== typeof v && "number" !== typeof v || null === v) v = "none";
        switch (v.toString().toLowerCase()) {
            case "10": case "nosourcecode": return 10;
            case "20": case "withsourcecode": return 20;
            default: return 0;
        }
    }
};

function isEmpty(v) {
    var _tp = typeof v;
    if (null !== v && "object" === _tp) return false;
    if ('undefined' === _tp || null === v) return true;
    if ("string" === _tp && ("" === v.trim() || "\t" === v)) return true;
    return false;
}

function ifEmpty(v, def) {
    if (isEmpty(v)) return def;
    return v;
}

// --- url fns

App.url = App.url || {};
App.url.setParams = function (url, params, toFixDuplicates) {
    if ("undefined" === typeof url || null === url || "" === url) return "";
    //trace("App.url.setParams() 20: params.length:", params.length);
    if ("undefined" === typeof params || null === params) return url;
    var s = url;
    for (var name in params) {
        if ("undefined" === typeof name || null === name || "" === name || "undefined" === typeof params[name] || null === params[name]) continue;
        s = App.url.setParam(s, name, params[name], toFixDuplicates);
    }
    return s;
};
App.url.setParam = function (url, name, value, toFixDuplicates) {
    //trace("App.url.setParam() start:");
    var result = "";
    if (isEmpty(url)) url = "";
    if (isEmpty(name)) {
        result = url;
        //trace("App.url.setParam() 4: result:", result);
        return url;
    }
    if (isEmpty(url)) {
        result = "?" + name + "=" + value;
        //trace("App.url.setParam() 6: result:", result);
        return result;
    }
    if ("undefined" === typeof toFixDuplicates) toFixDuplicates = true;
    var path = "", s = "", hash = "";
    var posQ = url.indexOf("\?");
    var posH = url.indexOf("\#");
    //trace("App.url.setParam() 50: posQ:" + posQ + " posH:", posH);
    if (posQ > posH && posH !== -1) posQ = -1;
    if (posQ > -1) {
        path = url.substr(0, posQ);
        s = url.substr(posQ);
    }
    else {
        if (posH > -1) {
            path = url.substr(0, posH);
            s = url.substr(posH);
        }
        else path = url;
    }
    if (1 >= s.length) {
        result = path + "?" + name + "=" + value;
        //trace("App.url.setParam() 100: end: result:" + result);
        return result;
    }
    var c = s.substr(0, 1);
    s = s.substr(1);
    //trace("App.url.setParam() 200: c:" + c + " s:" + s);
    if ("\#" === c) {
        hash = s;
        result = path + "?" + name + "=" + value + "#" + s;
        //trace("App.url.setParam() 250: end: result:" + result);
        return result;
    }
    posH = s.indexOf("\#");
    if (posH !== -1) {
        hash = s.substr(posH + 1);
        s = s.substr(0, posH);
    }
    //trace("App.url.setParam() 400: path:" + path + " s:" + s + " hash:", hash);
    if ("" !== hash) hash = "#" + hash;
    if ("" === s) {
        result = path + "?" + name + "=" + value + hash;
        //trace("App.url.setParam() 450: end: result:" + result);
        return result;
    }
    //trace("App.url.setParam() 500:");
    var _foundSameParam = false;
    var params = s.split("&");
    var qs = "", posEQ = "", pName = "", pValue = "";
    for (var i = 0; i < params.length; i++) {
        var p = params[i].trim();
        if ("" === p) continue;
        pName = "", pValue = "";
        posEQ = p.indexOf("=");
        if (-1 === posEQ) pName = p;
        else {
            pName = p.substr(0, posEQ);
            pValue = p.substr(posEQ + 1);
        }
        //trace("App.url.setParam() 600: pName:" + pName + " pValue:" + pValue);
        if (pName === name) {
            _foundSameParam = true;
            pValue = value;
        }
        if ("" !== qs) qs += "&";
        qs += pName + "=" + pValue;
    }
    if (!_foundSameParam) {
        if ("" !== qs) qs += "&";
        qs += name + "=" + value;
    }
    result = path + "?" + qs + hash;
    //trace("App.url.setParam() 800: result:" + result);
    if (toFixDuplicates) result = App.url.fixDuplicatedParams(result);
    //trace("App.url.setParam() 900: end: result:" + result);
    return result;
};
App.url.fixDuplicatedParams = function (url) {
    //trace("App.url.fixDuplicatedParams() start:");
    if (isEmpty(url)) {
        //trace("App.url.fixDuplicatedParams() 5: result empty;");
        return "";
    }
    var path = "", s = "", hash = "";
    var posQ = url.indexOf("\?");
    var posH = url.indexOf("\#");
    //trace("App.url.fixDuplicatedParams() 50: posQ:" + posQ + " posH:", posH);
    if (posQ > posH && posH !== -1) posQ = -1;
    if (posQ > -1) {
        path = url.substr(0, posQ);
        s = url.substr(posQ);
    }
    else {
        if (posH > -1) {
            path = url.substr(0, posH);
            s = url.substr(posH);
        }
        else path = url;
    }
    var result = "";
    if (1 >= s.length) {
        result = path;
        //trace("App.url.fixDuplicatedParams() 100: end: result:" + result);
        return result;
    }
    var c = s.substr(0, 1);
    s = s.substr(1);
    //trace("App.url.fixDuplicatedParams() 200: c:" + c + " s:" + s);
    if ("\#" === c) {
        hash = s;
        result = path + "#" + s;
        //trace("App.url.fixDuplicatedParams() 250: end: result:" + result);
        return result;
    }
    posH = s.indexOf("\#");
    if (posH !== -1) {
        hash = s.substr(posH + 1);
        s = s.substr(0, posH);
    }
    //trace("App.url.fixDuplicatedParams() 400: path:" + path + " s:" + s + " hash:", hash);
    if ("" !== hash) hash = "#" + hash;
    if ("" === s) {
        result = path + hash;
        //trace("App.url.fixDuplicatedParams() 450: end: result:" + result);
        return result;
    }
    //trace("App.url.fixDuplicatedParams() 500:");
    var params = s.split("&");
    var arr = new Array();
    var posEQ = "", pName = "", pValue = "";
    var i = 0;
    for (i = 0; i < params.length; i++) {
        var p = params[i].trim();
        if ("" === p) continue;
        pName = "", pValue = "";
        posEQ = p.indexOf("=");
        if (-1 === posEQ) pName = p;
        else {
            pName = p.substr(0, posEQ);
            pValue = p.substr(posEQ + 1);
        }
        //trace("App.url.fixDuplicatedParams() 600: pName:" + pName + " pValue:" + pValue);
        arr[arr.length] = { name: pName, value: pValue };
    }
    // delete older params
    for (i = 0; i < arr.length; i++) {
        if (null === arr[i]) continue;
        var x = i + 1;
        for (var j = x; j < arr.length; j++) {
            if (null === arr[j] || null === arr[i]) continue;
            if (arr[i].name === arr[j].name) arr[i] = null;
        }
    }
    var qs = "";
    for (i = 0; i < arr.length; i++) {
        if (null === arr[i]) continue;
        if ("" !== qs) qs += "&";
        qs += arr[i].name + "=" + arr[i].value;
    }
    result = path + "?" + qs + hash;
    //trace("App.url.fixDuplicatedParams() 900: end: result:" + result);
    return result;
};
App.url.removeHash = function (url) {
    if (isEmpty(url)) return "";
    var _x = url.indexOf("#");
    if (_x === -1) return url;
    url = url.left(_x);
    return url;
};
App.url.removeParam = function (url, name) {
    if (isEmpty(url)) return "";
    if (isEmpty(name)) return url;
    name = name.toLowerCase();
    var _url = url.toLowerCase();
    var _x = _url.indexOf("\?" + name);
    if (_x === -1) {
        _x = _url.indexOf("\&" + name);
        if (_x === -1) return url;
    }
    var _s1 = url.substr(0, _x);
    var _s2 = "";
    var _y = url.indexOf("\&", _x + 1);
    if (-1 !== _y) {
        _s2 = url.substr(_y + 1);
        if ("" !== _s2) {
            var _s = _s1.right(1);
            if (_s !== "\?" && _s !== "\&") _s1 += "\?";
        }
    }
    if ("" === _s2) {
        _y = url.indexOf("\#", _x + 1);
        if (-1 !== _y) _s2 = url.substr(_y);
    }
    url = _s1 + _s2;
    //trace("App.url.removeParam() end: name:[" + name + "] url:", url);
    return url;
};
App.url.getAllParams = function (url) {
    // returns array of [url param objects]: { name, value } from url
    // ?s=1&d=3&f=1 => Array [ { name:"s", value:"1" }, { name:"d", value:"3" }, ... ]
    var _params = new Array();
    if ("undefined" === typeof url || null === url) return _params;
    url = url.toString().trim(); if ("" === url) return _params;
    if ("undefined" === typeof name || null === name) return _params;
    name = name.toString().trim().toLowerCase(); if ("" === name) _params;
    var _qs = null;
    var _x = url.indexOf("?");
    if (-1 === _x) _qs = url; else _qs = url.substr(_x + 1);
    _x = _qs.indexOf("=");
    if (-1 === _x) return _params;
    var _arr = new Array();
    _x = url.indexOf("&");
    if (-1 === _x) _arr[0] = _qs; else _arr = _qs.split("&");
    var _v = "", _p = "";
    for (var i = 0; i < _arr.length; i++) {
        if (-1 === _arr[i].indexOf("=")) {
            _params[i] = { name: _arr[i].toLowerCase(), value: "" };
            continue;
        }
        _p = _arr[i].split("=");
        _v = "";
        if ("" === _p[1]) _v = "";
        else {
            // could be a=sdsd=sds
            for (var j = 1; j < _p.length; j++) {
                if ("" !== _v) _v += "=";
                _v += _p[j];
            }
            _v = decodeURIComponent(_v);
        }
        _params[i] = { name: _p[0].toLowerCase(), value: _v };
    }
    return _params;
};
App.url.getParam = function (url, name, params, mode) {
    // returns querystring parameter value
    // if query contains multiple params => returns all params separated by "|"
    // [params] -- array of quearyparam objects could be passed
    if ("undefined" === typeof mode || null === mode) mode = "";
    var _arr = new Array();
    if ("undefined" === typeof params || null === params || "undefined" === typeof params.length || 1 > params.length) {
        params = App.url.getAllParams(url);
        if (1 > params.length) {
            if ("onlyfirst" === mode) return "";
            else return _arr;
        }
    }
    try {
        for (var i = 0; i < params.length; i++) {
            if (params[i].name === name) {
                _arr.push(params[i].value);
            }
        }
    }
    catch (e) {
        trace("App.url.getParam() err: ", e, "error");
    }
    if ("onlyfirst" === mode) {
        if (_arr.length === 0) return null;
        else return _arr[0];
    }
    return _arr;
};
App.url.getFileName = function (url) {
    // !! file should have extention
    // works also with invalid urls like: ...//aaa.bbb.сс/file.jpg?p=vvv#dsfdsf=dsfdsf&df=sdfsdf
    // //aaa.bbb.ccc/file.jpg, aaa.bbb.сс/file.jpg?p=vvv, ...
    if ("undefined" === typeof url || null === url || "" === url.trim()) return "";
    var s = url.toString().trim();
    if ("" === s) return "";
    s = s.replace(/\\/g, "/");
    var x = -1;
    if ("https://" === s.substring(0, 8).toLowerCase()) s = s.substr(8);
    else if ("http://" === s.substring(0, 7).toLowerCase()) s = s.substr(7);
    else if ("://" === s.substring(0, 3)) s = s.substr(3);
    else if ("//" === s.substring(0, 2)) s = s.substr(2);
    s = s.trim();
    if ("" === s) return "";
    s = s.replace(/\/\/\/\//g, "\/").replace(/\/\/\//g, "/").replace(/\/\//g, "/");
    var y = -1;
    x = s.length;
    y = s.toString().indexOf("?"); if (-1 !== y && x > y) x = y;
    y = s.toString().indexOf("#"); if (-1 !== y && x > y) x = y;
    y = s.toString().indexOf("&"); if (-1 !== y && x > y) x = y;
    y = s.toString().indexOf("="); if (-1 !== y && x > y) x = y;
    s = s.substring(0, x);
    x = s.lastIndexOf("/");
    if (x === 0) { // /file.ext
        if (4 > s.length) return "";
    }
    else
        if (-1 === x || x === s.length - 1) return ""; // xxx || xxx/
    s = s.substr(x + 1);
    if (3 > s.length) return "";// xx
    if (-1 === s.indexOf("\.")) return "";
    return s;
};

function isInt(n) {
    return typeof n === 'number' && n % 1 === 0;
}

function toInteger(o, def, min) {
    if ("undefined" === typeof def) def = 0;
    if ("undefined" === typeof o || null === o) return def;
    var _n = parseInt(o, 0);
    if (isNaN(_n)) return def;
    if ("undefined" !== typeof min && null !== min && min > _n) return min;
    return _n;
}

function toFloat(o, def, min) {
    if ("undefined" === typeof def) def = 0;
    if ("undefined" === typeof o || null === o) return def;
    if ("string" === typeof o) o = o.replace(",", ".");
    var _n = parseFloat(o);
    if (isNaN(_n)) return def;
    if ("undefined" !== typeof min && null !== min && min > _n) return min;
    return _n;
}

function toLocalMoney(v, def, allowOnlyPositive, currSign, addCurrSign, addCurrBefore, showZeros) {
    if ("undefined" === typeof def || null === def) return def = "";
    if ("undefined" === typeof v || null === v) return def;
    if ("string" === typeof v) v = v.replace(",", ".");
    var f = parseFloat(v);
    if (isNaN(f)) return def;
    if ("undefined" === typeof allowOnlyPositive || null === allowOnlyPositive) allowOnlyPositive = false;
    if (allowOnlyPositive & f <= 0) return def;

    if ("undefined" === typeof currSign || null === currSign) currSign = "";
    if ("undefined" === typeof addCurrSign || null === addCurrSign) addCurrSign = true;
    if ("undefined" === typeof addCurrBefore || null === addCurrBefore) addCurrBefore = true;
    if ("undefined" === typeof showZeros || null === showZeros) showZeros = true;

    if (!addCurrSign) currSign = "";

    var s = "";
    try {
        if (showZeros) s = f.toLocaleString(undefined, { style: "decimal", 'minimumFractionDigits': 2, 'maximumFractionDigits': 2 });
        else s = f.toLocaleString(undefined, { style: "decimal", 'maximumFractionDigits': 2 });
    }
    catch (e) {
        if (showZeros) s = f.toFixed(2);
        else s = f.toString();
    }

    var result = addCurrBefore ? currSign + s : s + currSign;

    //trace("f: " + f + " (" + typeof f + ") , result: ", result);

    return result;
}

function toBool(v, def) {
    if ("undefined" === typeof def || null === def) def = false;
    if ("undefined" === typeof v || null === v) return def;
    if (true === v || false === v) return v;
    v = v.toString().toLowerCase().trim();
    if ("" !== v && "false" !== v && "0" !== v) return true;
    return false;
}

function isValidEmail(v) {
    if ("undefined" === typeof v || null === v || "" === v.toString().trim()) return false;
    // skippables v
    //return /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()\.,;\s@\"]+\.{0,1})+([^<>()\.,;:\s@\"]{2,}|[\d\.]+))$/.test(v.toString().trim());
    // appbuild v
    //return /^([a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+(\.[a-z\d!#$%&'*+\-\/=?^_`{|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)*|"((([ \t]*\r\n)?[ \t]+)?([\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*(([ \t]*\r\n)?[ \t]+)?")@(([a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\d\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.)+([a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF][a-z\d\-._~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]*[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])\.?$/i.test(v);
    var s = "^([a-z\\d!#$%&\'*+\\-\\/=?^_`{|}~\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]+(\\.[a-z\\d!#$%&\'*+\\-\\/=?^_`{|}~\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]+)*|\"((([ \\t]*\\r\\n)?[ \\t]+)?([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f\\x21\\x23-\\x5b\\x5d-\\x7e\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))*(([ \\t]*\\r\\n)?[ \\t]+)?\")@(([a-z\\d\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[a-z\\d\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF][a-z\\d\\-._~\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]*[a-z\\d\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])\\.)+([a-z\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]|[a-z\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF][a-z\\d\\-._~\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]*[a-z\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])\\.?$";
    var re = new RegExp(s, "i");
    return re.test(v);
}

function getFunctionName(o, defaultName) {
    if ("undefined" === typeof defaultName || null === defaultName) defaultName = "-NOT-FUNCTION-";
    if ("function" !== typeof o) return defaultName;
    var _ret = o.toString();
    _ret = _ret.substr('function '.length);
    _ret = _ret.substr(0, _ret.indexOf('('));
    return _ret;
}

function trace(s1, s2, mode) {
    if ("undefined" === typeof window.showDebugInfo || !window.showDebugInfo) return;
    try {

        if ("undefined" === typeof console || "undefined" === typeof console.log) return;
        if ("undefined" === typeof mode || null === mode) mode = "";
        var s = "";
        if ("undefined" === typeof s1) { s1 += "[-undefined-]"; s += s1; }
        else if (null === s1) { s1 = "[-null-]"; s += s1; }
        else if (false === s1) { s1 = "[-false-]"; s += s1; }
        else if (true === s1) { s1 = "[-true-]"; s += s1; }
        else if ("" === s1) { s1 = "[-empty-]"; s += s1; }
        else if (Array.isArray(s1)) {
            switch (mode) {
                case "info": console.info("[array] -v"); console.log("%o", s1); break;
                case "trace": console.trace("[array] -v"); console.log("%o", s1); break;
                case "warn": console.warn("[array] -v"); console.log("%o", s1); break;
                case "error": console.error("[array] -v"); console.log("%o", s1); break;
                default: console.log("[array] -v"); console.log("%o", s1); break;
            }
        }
        else if ("object" === typeof s1) {
            switch (mode) {
                case "info": console.info("[object] -v"); console.log("%o", s1); break;
                case "trace": console.trace("[object] -v"); console.log("%o", s1); break;
                case "warn": console.warn("[object] -v"); console.log("%o", s1); break;
                case "error": console.error("[object] -v"); console.log("%o", s1); break;
                default: console.log("[object] -v"); console.log("%o", s1); break;
            }
        }
        else if ("function" === typeof s1) {
            s += "[function] " + getFunctionName(s1) + "()";
        }
        else s += s1;

        if ("undefined" === typeof s2) {
            if (arguments.length > 1) { s2 = "[-undefined-]"; s += " " + s2; }
            s2 = "";
        }
        else if (null === s2) { s2 = "[-null-]"; s += " " + s2; }
        else if (false === s2) { s2 = "[-false-]"; s += s2; }
        else if (true === s2) { s2 = "[-true-]"; s += s2; }
        else if ("" === s2) { s2 = "[-empty-]"; s += " " + s2; }
        else if (Array.isArray(s2)) {
            s += " [array] -v";
            switch (mode) {
                case "info": console.info(s); break;
                case "trace": console.trace(s); break;
                case "warn": console.warn(s); break;
                case "error": console.error(s); break;
                default: console.log(s); break;
            }
            s = "";
            console.log("%o", s2);
            return;
        }
        else if ("object" === typeof s2) {
            s += " [object] -v";
            switch (mode) {
                case "info": console.info(s); break;
                case "trace": console.trace(s); break;
                case "warn": console.warn(s); break;
                case "error": console.error(s); break;
                default: console.log(s); break;
            }
            s = "";
            console.log("%o", s2);
            return;
        }
        else if ("function" === typeof s2) {
            s += " [function] " + getFunctionName(s2) + "()";
        }
        else s += " " + s2;

        s = s.trim();
        if ("" !== s) {
            switch (mode) {
                case "info": console.info(s); break;
                case "trace": console.trace(s); break;
                case "warn": console.warn(s); break;
                case "error": console.error(s); break;
                default: console.log(s); break;
            }
        }
    }
    catch (e) {
        if ("undefined" !== typeof console && null !== console && "undefined" !== typeof console.error && null !== console.error) console.error(e);
    }
}

function isValidSimpleName(v, allowSpaces, min, max, allowStartNumbers) {
    if ("undefined" === typeof v || null === v || "" === v) return false;
    if ("undefined" === typeof allowSpaces || null === allowSpaces) allowSpaces = true;
    if ("undefined" === typeof allowStartNumbers || null === allowStartNumbers) allowStartNumbers = false;
    if ("number" !== typeof min || null === min || 1 > min) min = 0;
    if ("number" !== typeof max || null === max || 1 > max) max = 0;
    v = v.trim();
    if (v.length < min || (v.length > max && max > 0)) return false;
    var reStr = "";
    if (allowStartNumbers) {
        if (allowSpaces) reStr = "^[a-zA-Z0-9]([a-zA-Z0-9_\\- ]*)$";
        else reStr = "^[a-zA-Z0-9]([a-zA-Z0-9_\\-]*)$";
    }
    else {
        if (allowSpaces) reStr = "^[a-zA-Z]([a-zA-Z0-9_\\- ]*)$";
        else reStr = "^[a-zA-Z]([a-zA-Z0-9_\\-]*)$";
    }
    var re = new RegExp(reStr, "gi");
    var valid = re.test(v);
    //trace("head.isValidSimpleName() end: valid:", valid);
    return valid;
}

function isValidVersionStr(v) {
    if ("undefined" === typeof v || null === v || "" === v) return false;
    var valid = /^[0-9]+(\.[0-9]+)+$/g.test(v.trim());
    //trace("head.isValidVersion() end: valid:", valid);
    return valid;
}

function isValidRelFolderPath(v, allowNullEmpty) {
    allowNullEmpty = ("undefined" === typeof allowNullEmpty || null === allowNullEmpty || allowNullEmpty === false) ? false : true;
    if ("undefined" === typeof v || null === v || "" === v) return allowNullEmpty;
    v = v.trim().replace(/\\/g, "/");
    var re = new RegExp("([a-zA-Z0-9_\\-]+[\\/])*[a-zA-Z0-9_\\-]+$", "g");
    return re.test(v);
}

function isValidRelFilePath(v, fileExt, allowNullEmpty) {
    allowNullEmpty = ("undefined" === typeof allowNullEmpty || null === allowNullEmpty || allowNullEmpty === false) ? false : true;
    if ("undefined" === typeof v || null === v || "" === v) return allowNullEmpty;
    v = v.trim().replace(/\\/g, "/");
    var re = new RegExp("([a-zA-Z0-9_\\-]+[\\/])*[a-zA-Z0-9_\\-]+\\." + fileExt + "$", "gi");
    return re.test(v);
}

/*
function isValidUtfString(v, allowNullEmpty) {
    allowNullEmpty = ("undefined" === typeof allowNullEmpty || null === allowNullEmpty || allowNullEmpty === false) ? false : true;
    if ("undefined" === typeof v || null === v || "" === v) return allowNullEmpty;
    var re = new RegExp("^[ -_\'\?\!0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]$","u");
    v = v.trim();
    if ("" === v) return allowNullEmpty;
    return re.test(v);
}
*/

function disableElement(o, id) {
    if (null === o) o = document.getElementById(id);
    if (null === o) return;
    o.disabled = true;
    o.setAttribute("disabled", "");
    return o;
}

function enableElement(o, id) {
    if (null === o) o = document.getElementById(id);
    if (null === o) return;
    o.disabled = false;
    o.removeAttribute("disabled");
    return o;
}

function add0(v, requiredLength) {
    if ("undefined" === typeof v || null === v) return "00";
    try {
        v = v.toString().trim();
        var x = requiredLength - v.length;
        for (var i = 0; i < x; i++) v = "0" + v;
    }
    catch (e) {
        //trace("add0() err:",e,"error");
        return v;
    }
    return v;
}

function getUTCParser(str, swapDayMonth) {
     var arr = str.split(/[- :.\\\/]/);
    var utc;
    if (swapDayMonth) utc = new Date(arr[2], arr[1] - 1, arr[0], arr[3], arr[4], arr[5]);
    else utc = new Date(arr[2], arr[0] - 1, arr[1], arr[3], arr[4], arr[5]);
    utc.setTime(utc.getTime() - utc.getTimezoneOffset() * 60 * 1000);
    return utc;
}

function getUTC(str) {
    var utc = new Date(Date.parse(str));
    utc.setTime(utc.getTime() - utc.getTimezoneOffset() * 60 * 1000);
    return utc;
}

function dateStrToLocaleStr(str) {
    if ("undefined" === typeof str || null === str || "" === str) return "-empty-date-";
    var d = null;
    try { d = new Date(Date.parse(str)); }
    catch (e) { return str; }
    if (null === d) return str;
    return d.toLocaleString();//.replace(/GMT.*/g, "");
}

function dateToLocaleStr(d) {
    if ("undefined" === typeof d || null === d) return "-empty-date-";
    try { return d.toLocaleString(); }
    catch (e) { return "-invaid-date-"; }
}

function utcToLocaleStr(str) {
    if ("undefined" === typeof str || null === str || "" === str) return "-empty-date-";
    var d = null;
    try { d = new Date(Date.parse(str)); }
    catch (e) { return str; }
    if (null === d) return str;
    d = new Date(d.getTime() - d.getTimezoneOffset() * 60 * 1000);
    //trace("utcToLocaleStr() 300:  str: " + str + "  => d: " + d + " => result: " + result);
    return d.toLocaleString();//.replace(/GMT.*/g, "");
}

function utcDateToISO8601Str(d) {
    // returns ~ 2019-11-25T19:27:27
    if ("undefined" === typeof d || null === d) return "Invalid date";
    var result = d.getUTCFullYear() + "-" + add0(d.getUTCMonth() + 1, 2) + "-" + add0(d.getUTCDate(), 2);
    result += "T" + add0(d.getUTCHours(), 2) + ":" + add0(d.getUTCMinutes(), 2) + ":" + add0(d.getUTCSeconds(), 2);
    return result;
}

function dateToISO8601Str(d) {
    // returns ~ 2019-11-25T19:27:27
    if ("undefined" === typeof d || null === d) return "Invalid date";
    var result = d.getFullYear() + "-" + add0(d.getMonth() + 1, 2) + "-" + add0(d.getDate(), 2);
    result += "T" + add0(d.getHours(), 2) + ":" + add0(d.getMinutes(), 2) + ":" + add0(d.getSeconds(), 2);
    return result;
}

function getOffset(el) {
    var x = 0;
    var y = 0;
    while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
        x += el.offsetLeft - el.scrollLeft;
        y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: y, left: x };
}

function isSafari() {
    var ua = window.navigator.userAgent.toLowerCase();
    return ua.indexOf('safari') >= 0 && ua.indexOf('chrome') < 0 && ua.indexOf('android') < 0;
}

function blink(o, n, timeout) {
    if ("undefined" === typeof o || null === o) return;
    if ("number" !== typeof n) n = 2;
    if (1 > n) return;
    o = $(o);
    if ("undefined" === typeof timeout || null === timeout || 1 > timeout) timeout = 0;
    setTimeout(function () {
        o.fadeOut(100, function () {
            o.fadeIn(100, function () {
                blink(o, n - 1);
            });
        });
    }, timeout);
}

function getAttribute(o, name, def, dataType) {
    // checks & returns dom attribute value
    // [o]: dom or JQ object
    // [name]: attribute name
    // [def]: default value
    // [dataType]: data-type of value

    if ("undefined" === typeof def) def = "";
    if ("undefined" === typeof o || null === o || "undefined" === typeof name || "" === name) return def;
    var e = null;
    if ("number" === typeof o.length) {
        if ("object" !== typeof o[0] || null === o[0]) return def;
        e = o[0];
    }
    else e = o;
    var v = e.getAttribute(name);
    if ("string" !== typeof v || "" === v) return def;
    v = v.trim();
    if ("" === v) return def;
    if ("string" !== typeof dataType) dataType = "";
    else dataType = dataType.trim().toLowerCase();
    switch (dataType) {
        case "float": case "decimal": case "num": v = toFloat(v, def, def); break;
        case "int": case "integer":  v = toInteger(v, def, def); break;
        case "bool": case "boolean": v = toBool(v, def); break;
    }
    return v;
}

function traceFormData(formData, prefix) {
    if ("function" !== typeof trace) return;
    if ("undefined" === typeof prefix || null === prefix) prefix = "";
    if (!(formData instanceof FormData)) {
        trace(prefix + " traceFormData() : !! Is not instance of FormData !");
        return;
    }
    try {
        trace(prefix + " ===> FormData()");
        for (let [key, value] of formData.entries()) trace(' - ' + key + ' :', value);
        trace(prefix + " <=== FormData()");
    }
    catch (e) {
        trace('traceFormData() err: ', e, 'error');
    }
}

ghead.isSafari = isSafari();
trace("head 22100: --- ghead.isSafari: ", ghead.isSafari);

ghead.SVG = ghead.SVG || {};
ghead.SVG.supported_Image = false;
ghead.SVG.checkSupport = function (callback) {
    // detects if browser supports SVG images. !!! => get Result in [ghead.SVG.supported].
    try { ghead.SVG.supported_Image = document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1"); } catch (e) { ; }
    if (ghead.SVG.supported_Image) {
        trace("head 23100: --- [by feature] .SVG.supported_Image: ", true);
        return;
    }

    var img = new Image();
    img.onload = function () {
        var result = img.width > 0 && img.height > 0;
        ghead.SVG.supported_Image = result;
        trace("head 23200: --- [by image] .SVG.supported_Image: ", ghead.SVG.supported_Image);
        if ("function" === typeof callback) callback(result);
    };
    img.onerror = function (e) {
        ghead.SVG.supported_Image = false;
        trace("head 23300: --- .SVG.supported_Image: [-false-]");
        //trace("head 919500: .SVG.supported_Image: [-false-]. err: ", e);
        if ("function" === typeof callback) callback(false);
    };
    //img.src = "data:image/svg+xml;base64,QQQ"; // bad file to test error
    //img.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D";
    img.src = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==";
};
ghead.SVG.checkSupport(null);

ghead.SVG.svgInline = function () {
    // downloads SVG source (for SVG Images with class "svg-inline") and replaces IMG tag with SVG tag
    // used for CSS styling, coloring, ...
    $('img[src*=".svg"].svg-inline').each(function () {
        var $img = $(this);
        var imgURL = $img.attr('src');
        var attributes = $img.prop("attributes");
        $.get(imgURL, function (data) {
            try {
                var $svg = $(data).find('svg');
                $svg = $svg.removeAttr('xmlns:a');
                $.each(attributes, function () { $svg.attr(this.name, this.value); });
                $svg.find('title').remove();
                $svg.find('#icomoon-ignore').remove();
                $img.replaceWith($svg);
            }
            catch (e) { ; }
        }, 'xml');
    });
};

ghead.onHistoryBackForward = function (e) {
    var _stackPath = "ghead.onHistoryBackForward()";
    trace(_stackPath + ".wasHistoryBackForward: ", ghead.wasHistoryBackForward);
    //trace(_stackPath + " e: ", e);
};

// history back/forward detector
window.addEventListener("pageshow", function (e) {
    try {
        ghead.wasHistoryBackForward = e.persisted || (typeof window.performance !== "undefined" && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD);
        if (ghead.wasHistoryBackForward) ghead.onHistoryBackForward(e);
    }
    catch (e) { ; }
});

// ----- Jquery extend -->

function addJQExtends($, n) {
    if ("undefined" === typeof n || null === n) { n = 1; } else { n = parseInt(n, 0); if (isNaN(n)) n = 1; }
    //trace(".addJQExtends() 10: try n:"+n);
    if (20 < n) return;
    if ("undefined" === typeof $ || null === $) {
        setTimeout(function () { addJQExtends($, n + 1); }, 10);
        return;
    }
    if ("undefined" === typeof window.showDebugInfo || !window.showDebugInfo) {
        trace(".addJQExtends() 90: -- do not show debug info. > !window.showDebugInfo");
        $.fn.addBindInfo = function () { return this; };
        $.fn.setBindInfo = function () { return this; };
        $.fn.getBindInfo = function () { return new Array(); };
        return;
    }
    trace(".addJQExtends() 100:");
    // add jquery [BindInfo] functions
    // .BindInfo
    // -- uset to add to DOM element attribute "data-bind-info" with name of binded funcion or other help info > "click:skipbl.my.function123"
    $.fn.addBindInfo = function (v) {
        // appends string to current value
        return this.each(function (o) {
            try {
                if ("function" !== typeof this.setAttribute) {
                    //trace("$.fn.addBindInfo() err: 100: not DOM element! v:", v);
                    //trace("$.fn.addBindInfo() :", this);
                    return;
                }
                var _elem = $(this), _nm = "data-bind-info";
                _elem.attr(_nm, (_elem.attr(_nm) ? _elem.attr(_nm) + "; " : "") + v);
            }
            catch (e) {
                trace("$.fn.addBindInfo() err: 200: v:", v);
                trace("$.fn.addBindInfo() :", e);
                trace("$.fn.addBindInfo() :", this);
            }
        });
    };
    // -- uset to add to DOM element attribute "data-bind-info" with name of binded funcion or other help info > "click:skipbl.my.function123"
    $.fn.setBindInfo = function (v) {
        // rewrites current value
        return this.each(function (o) {
            try {
                if ("function" !== typeof this.setAttribute) {
                    //trace("$.fn.setBindInfo() err: 100: not DOM element! v:", v);
                    //trace("$.fn.setBindInfo() :", this);
                    return;
                }
                var _elem = $(this), _nm = "data-bind-info";
                _elem.attr(_nm, v);
            }
            catch (e) {
                trace("$.fn.setBindInfo() err: 200: v:", v);
                trace("$.fn.setBindInfo() :", e);
                trace("$.fn.setBindInfo() :", this);
            }
        });
    };
    $.fn.getBindInfo = function (v) {
        // returns array of strings
        var _arr = new Array();
        this.each(function () {
            try {
                if ("function" !== typeof this.setAttribute) {
                    //trace("$.fn.getBindInfo() err: 100: not DOM element! v:", v);
                    //trace("$.fn.getBindInfo() :", this);
                    return _arr;
                }
                var _elem = $(this), _nm = "data-bind-info";
                _arr.push(_elem.attr(_nm, v));
            }
            catch (e) {
                trace("$.fn.getBindInfo() err: 200: v:", v);
                trace("$.fn.getBindInfo() :", e);
                trace("$.fn.getBindInfo() :", this);
            }
        });
        return _arr;
    };

    // removes from DOM element classes containing [mask]
    $.fn.removeClassLike = function (mask) {
        if ("undefined" === typeof this.className) {
            //trace("$.fn.removeClassLike() err: 100: not DOM element!", this);
            return;
        }
        return this.removeClass(function (index, cls) {
            var re = mask.replace(/\*/g, '\\S+');
            return (cls.match(new RegExp('\\b' + re + '', 'g')) || []).join(' ');
        });
    };

    // remove from DOM element all classes from [p] param (could be array or string)
    $.fn.removeClasses = function (p) {
        return this.each(function (o) {
            if ("undefined" === typeof this.className) {
                //trace("$.fn.removeClasses() err: 100: not DOM element!", this);
                return;
            }
            if ("undefined" === typeof p || null === p) return;
            if ("string" === typeof p) {
                if (p.indexOf(" ") > -1) p = p.split(" ");
                else if (p.indexOf(",") > -1) p = p.split(",");
                else { p = (new Array()).push(p); }
            }
            if (!Array.isArray(p) || p.length < 1) return;
            if ("undefined" === typeof this.className) {
                trace("$.fn.removeClasses() err: 100: not DOM element!", this);
                return;
            }
            var _elem = $(this);
            for (var i = 0; i < p.length; i++) {
                if ("undefined" === typeof p[i] || null === p[i] || "" === p[i]) continue;
                //trace("$.fn.removeClasses() 200: p[i]:", p[i]);
                _elem.removeClass(p[i]);
            }
        });
    };
}

addJQExtends($, 1);

// < ----- Jquery extend --

var gmouse = gmouse || {};
gmouse.X = 0;
gmouse.Y = 0;

gmouse.init = function ($, n) {
    // attaches event, that fills current coordinates of mouse. (useful for loaders, tooltips, ...)
    if ("undefined" === typeof n || null === n) { n = 1; } else { n = parseInt(n, 0); if (isNaN(n)) n = 1; }
    //trace(".gmouse.init() 10: try n:"+n);
    if (20 < n) return;
    if ("undefined" === typeof $ || null === $) {
        setTimeout(function () { gmouse.init($, n + 1); }, 100);
        return;
    }
    if ("function" !== typeof $.fn.addBindInfo) {
        setTimeout(function () { gmouse.init($, n + 1); }, 100);
        return;
    }
    var body = $("body");
    if (1 > body.length) {
        setTimeout(function () { gmouse.init($, n + 1); }, 100);
        return;
    }
    trace(".gmouse.init() 100:");
    body.mousemove(function (e) {
        gmouse.X = e.pageX;
        gmouse.Y = e.pageY;
        //trace("gmouse: " + gmouse.X +", " + gmouse.Y);
    }).addBindInfo("mousemove: .gmouse.init 989:");
};

var gloading = gloading || {};

gloading.getPosition = function (e) {
    var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') === -1);
    var x = 0, y = 0, xFix = 0, yFix = 0;
    if (e) {
        xFix = e.clientHeight;
        if (0 < xFix) xFix = xFix / 2 - 50;
        if (0 > xFix || isNaN(xFix)) xFix = 0;

        yFix = e.clientWidth;
        if (0 < yFix) yFix = yFix / 2 - 50;
        if (0 > yFix || isNaN(yFix)) yFix = 0;
    }
    while (e) {
        x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
        y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
        e = e.offsetParent;
    }

    return { x: x + window.scrollX + xFix, y: y + window.scrollY + yFix };
};

gloading.get = function () {
    var _o = $("#gloading");
    if (1 > _o.length) {
        var _s = "";
        _s = '<div id="gloading" class="gloading" style="display:none">';
        _s += '<svg class="circular" viewBox="25 25 50 50">';
        _s += '<circle class="path" cx="50" cy="50" r="12" fill="none" stroke-width="3" stroke-miterlimit="12" />';
        _s += '</svg>';
        _s += '<div id="gloadingmsg" class="txt" style="display:none;"></div>';
        _s += '</div> ';
        _o = $(_s);
        $('body').append(_o);
    }
    return _o;
};

gloading.show = function (mode, obj, x, y, startTimeout, hideTimeout, txt, size) {
    var _sp = "gloading.show()";

    // [obj] used like event or target object!!
    if ("undefined" !== typeof startTimeout && null !== startTimeout && 0 < toInteger(startTimeout, 0)) {
        setTimeout(function () { gloading.show(mode, obj, x, y, 0); }, startTimeout);
        return;
    }
    if ("undefined" !== typeof hideTimeout && null !== hideTimeout && 0 < toInteger(hideTimeout, 0)) {
        setTimeout(function () { gloading.hide(0); }, hideTimeout);
    }
    //trace("gloading.show()");
    var o = gloading.get();
    o.addClass("active");
    if ("undefined" === typeof size || null === size) size = "";

    var loaderW = 60;

    switch (size) {
        case "small":
            loaderW = 50;
            o.removeClass("vsmall");
            o.addClass("small");
            break;
        case "vsmall":
            loaderW = 40;
            o.removeClass("small");
            o.addClass("vsmall");
            break;
        default:
            loaderW = o.width();
            o.removeClass("small");
            o.removeClass("vsmall");
    }
    mode = ifEmpty(mode, "");

    if ("undefined" !== typeof txt && null !== txt && "" !== txt.toString().trim()) {
        o.find(".txt").html(txt).show();
    }
    else o.find(".txt").hide();

    var jqObj = null;
    if ("undefined" === typeof obj || obj === null) obj = null;
    else {
        if ("undefined" === typeof obj.length) jqObj = $(obj);
        else jqObj = obj;
        if (1 > jqObj.length) jqObj = null;
        else if ("function" !== typeof jqObj.offset) jqObj = null;
    }
    o[0].style = "";
    if ("mouse" === mode) {
        o[0].style.position = "absolute";
        x = gmouse.X;
        y = gmouse.Y;
    }
    else if (("local" === mode || "localfixed" === mode ) && null !== jqObj) {
        // show loading in the middle of object
        if ("localfixed" === mode) o[0].style.position = "fixed";
        else o[0].style.position = "absolute";
        o[0].style.margin = "0";
        var parentW = 0;
        var parentH = 0;
        try {
            parentW = jqObj[0].clientWidth + jqObj[0].clientLeft;
            parentH = jqObj[0].clientHeight + jqObj[0].clientTop;
        }
        catch (e) {
            parentW = jqObj.width();
            parentH = jqObj.height();
        }
        var offset = getOffset(jqObj[0]);
        x = offset.left + (parentW / 2) - loaderW / 2 + 5;
        y = offset.top + (parentH / 2) - loaderW / 2;
    }
    else {
        // !! [obj] is event
        o[0].style.position = "fixed";
        if (("undefined" === typeof x || null === x) || ("undefined" === typeof y || null === y)) {
            x = 0; y = 0;
            if ("undefined" !== typeof obj && null !== obj) {
                if ("undefined" !== typeof obj.pageX && null !== obj.pageX) {
                    x = obj.pageX;
                    y = obj.pageY;
                }
                else {
                    var _target = obj.target || obj.srcElement;
                    if ("undefined" !== typeof _target && null !== _target && _target.nodeName !== "FORM") {
                        var _pos = gloading.getPosition(_target);
                        x = _pos.x;
                        y = _pos.y;
                    }
                }
            }
        }
    }
    if (x !== 0 && y !== 0) {
        o[0].style.top = y + "px";
        o[0].style.left = x + "px";
    }
    o[0].style.display = "inline-block";
};

gloading.hide = function (timeout) {
    if ("undefined" !== typeof timeout && null !== timeout && 0 < toInteger(timeout, 0)) {
        setTimeout(function () { gloading.hide(0); }, timeout);
        return;
    }
    //trace("gloading.hide()");
    var _o = gloading.get();
    _o.removeClass("active")[0].style.display = "none";
};

// --> galert : custom alert box used to show some info messages
var galert = galert || {};
galert.box = null;
galert.opened = false;
galert.get = function () {
    var box = $("#galert");
    if (1 > box.length) {
        box = document.createElement("DIV");
        box.id = "galert";
        box.style.display = "none";
        box.className = "galert alert alert-info";

        var txt = document.createElement("DIV");
        txt.id = "galert-txt";
        txt.className = "galert-txt";
        box.appendChild(txt);

        var btn = document.createElement("A");
        btn.id = "galert-btn-ok";
        btn.className = "galert-btn galert-btn-ok";
        btn.style.display = "none";
        btn.href = "javascript:void(0);";
        btn.click = function (e) { galert.hide(); };
        btn.text = "OK";
        btn.setAttribute("data-bind-info", "click: .galert.hide 64997:");
        box.appendChild(btn);

        $("body")[0].appendChild(box);

        box = $("#galert");
        if (1 > box.length) box = null;
    }
    galert.box = box;
    return box;
};

galert.hide = function () {
    //trace("galert.hide()");
    if (null === galert.box) return;// || galert.opened) return;
    galert.box[0].style.display = "none";
};

galert.show = function (mode, msg, autoCloseTimeout, showBtnOk) {
    //trace("galert.show()");
    if (null === galert.box) return false;

    if ("undefined" === typeof mode || null === mode) mode = "";
    var css = "galert ";
    switch (mode) {
        case "error": css += " alert-danger"; break;
        case "warning": css += " alert-warning"; break;
        case "success": css += " alert-success"; break;
        default: css += " alert-info";
    }

    //trace("  --- gmouse.X: ", gmouse.X);
    //trace("  --- gmouse.Y: ", gmouse.Y);

    galert.box[0].className = css;
    galert.box.find(">#galert-txt").html(msg);

    if (showBtnOk) galert.box.find(">#galert-btn-ok").show();
    else galert.box.find(">#galert-btn-ok").hide();

    var width = galert.box.width();
    var height = galert.box.height();
    var right = -1;

    var left = gmouse.X - width/2;
    var top = gmouse.Y - height - 10;

    if (left < 0) { left = 0; right = 0; }

    galert.box[0].style.left = left + "px";
    if (-1 !== right) galert.box[0].style.right = 0;
    else galert.box[0].style.right = "unset";

    galert.box[0].style.top = top + "px";

    //trace("  --- left: ", left);
    //trace("  --- top: ", top);

    galert.opened = true;

    galert.box.fadeIn(100, function () {
        galert.box.fadeOut(100, function () {
            galert.box.fadeIn(100, function () {
                var winWidth = document.documentElement.clientWidth;
                if ((galert.box[0].offsetLeft + width) > winWidth) {
                    galert.box[0].style.left = "0px";
                    galert.box[0].style.right = "0px";
                }

                if ("undefined" !== typeof autoCloseTimeout && null !== autoCloseTimeout && 0 < autoCloseTimeout) {
                    if (autoCloseTimeout < 1000) autoCloseTimeout = autoCloseTimeout * 1000;//consider it is seconds
                    setTimeout(function () {
                        galert.opened = false;
                        galert.hide();
                    }, autoCloseTimeout);
                }
            });
        });
    });
};

galert.init = function ($, n) {
    // attaches event to body, that closes alert on any click
    // creates [galert] element in body
    if ("undefined" === typeof n || null === n) { n = 1; } else { n = parseInt(n, 0); if (isNaN(n)) n = 1; }
    //trace(".galert.init() 10: try n:"+n);
    if (20 < n) return;
    if ("undefined" === typeof $ || null === $) {
        setTimeout(function () { galert.init($, n + 1); }, 100);
        return;
    }
    if ("function" !== typeof $.fn.addBindInfo) {
        setTimeout(function () { galert.init($, n + 1); }, 100);
        return;
    }
    var body = $("body");
    if (1 > body.length) {
        setTimeout(function () { galert.init($, n + 1); }, 100);
        return;
    }
    //trace(".galert.init() 90:");
    galert.get();
    if (null === galert.box) return;
    galert.box.off().on("click", function () { galert.hide(); }).addBindInfo("click: .galert.hide 53:");
    trace(".galert.init() 100:");
    body.click(function (e) { galert.hide(); }).addBindInfo("click: .galert.hide 683:");
};
// <-- galert

// --> gconfirm : custom confirm box with "ok/cancel" buttons.
var gconfirm = gconfirm || {};
gconfirm.box = null;
gconfirm.opened = false;
gconfirm.get = function () {
    var box = $("#gconfirm");
    if (1 > box.length) {
        box = document.createElement("DIV");
        box.id = "gconfirm";
        box.style.display = "none";
        box.className = "gconfirm alert alert-info";

        var txt = document.createElement("DIV");
        txt.id = "gconfirm-txt";
        box.appendChild(txt);

        var cancel = document.createElement("A");
        cancel.id = "gconfirm-cancel";
        cancel.className = "btn btn-cancel";
        cancel.text = "Cancel";
        cancel.setAttribute("data-defaulttext", cancel.text);
        cancel.href = "javascript:void(0)";
        box.appendChild(cancel);

        var ok = document.createElement("A");
        ok.id = "gconfirm-ok";
        ok.className = "btn btn-ok";
        ok.text = "Ok";
        ok.setAttribute("data-defaulttext", ok.text);
        ok.href = "javascript:void(0)";
        box.appendChild(ok);

        $("body")[0].appendChild(box);

        box = $("#gconfirm");
        if (1 > box.length) box = null;
        else {
            box.find(">#gconfirm-ok").on("click", function () { gconfirm.hide(); return false; }).setBindInfo("click: .gconfirm.hide");
            box.find(">#gconfirm-cancel").on("click", function () { gconfirm.hide(); return false; }).setBindInfo("click: .gconfirm.hide");
        }
    }
    gconfirm.box = box;
    return box;
};

gconfirm.restoreButtons = function () {
    //trace("gconfirm.restoreButtons()");
    if (null === gconfirm.box) return false;
    var btnOk = gconfirm.box.find(">#gconfirm-ok");
    if (0 < btnOk.length) btnOk[0].innerHTML = btnOk[0].getAttribute("data-defaulttext");
    var btnCancel = gconfirm.box.find(">#gconfirm-cancel");
    if (0 < btnCancel.length) btnCancel[0].innerHTML = btnCancel[0].getAttribute("data-defaulttext");
};

gconfirm.hide = function () {
    //trace("gconfirm.hide()");
    if (null === gconfirm.box || false === gconfirm.opened) return;
    gconfirm.box[0].style.display = "none";
    gconfirm.restoreButtons();
};

gconfirm.show = function (mode, msg, fnOK, fnCancel, autoCloseTimeout, okBtnText, cancelBtnText) {
    if (null === gconfirm.box) return false;

    var btnOk = gconfirm.box.find(">#gconfirm-ok");
    if (1 > btnOk.length) {
        trace("gconfirm.show()", "10: ERR: Not found [btnOk].", "error");
        return;
    }

    var btnCancel = gconfirm.box.find(">#gconfirm-cancel");
    if (1 > btnCancel.length) {
        trace("gconfirm.show()", "20: ERR: Not found [btnCancel].", "error");
        return;
    }

    if ("undefined" === typeof mode || null === mode) mode = "";
    var css = "gconfirm alert";
    switch (mode) {
        case "": break;
        case "error": css += " alert-danger"; break;
        case "warning": css += " alert-warning"; break;
        case "success": css += " alert-success"; break;
        case "red-confirm": css += " alert-danger red-confirm"; break;
        default: css += " alert-info";
    }

    //trace("  --- gmouse.X: ", gmouse.X);
    //trace("  --- gmouse.Y: ", gmouse.Y);

    gconfirm.box[0].className = css;
    gconfirm.box.find(">#gconfirm-txt").html(msg);

    if ("string" !== typeof okBtnText || okBtnText === "") okBtnText = "";
    if ("string" !== typeof cancelBtnText || cancelBtnText === "") cancelBtnText = "";
    if (okBtnText === "" || cancelBtnText === "") gconfirm.restoreButtons();
    if (okBtnText !== "") btnOk[0].innerHTML = okBtnText;
    if (cancelBtnText !== "") btnCancel[0].innerHTML = cancelBtnText;

    if ("function" === typeof fnOK) {
        btnOk.off().on("click", function () {
            gconfirm.hide();
            fnOK();
            return false;
        }).setBindInfo("click: gconfirm.show fnOK 347:");
    }
    else {
        btnOk.off().on("click", function () {
            gconfirm.hide();
            return false;
        }).setBindInfo("click: gconfirm.show fnOK 349:");
    }

    if ("function" === typeof fnCancel) {
        btnCancel.off().on("click", function () {
            gconfirm.hide();
            fnCancel();
            return false;
        }).setBindInfo("click: gconfirm.show fnCancel 357:");
    }
    else {
        btnCancel.off().on("click", function () {
            gconfirm.hide();
            return false;
        }).setBindInfo("click: gconfirm.show fnCancel 359:");
    }

    var width = gconfirm.box.width();
    var height = gconfirm.box.height();
    var right = -1;

    var left = gmouse.X - width / 2;
    var top = gmouse.Y - height - 10;

    if (left < 0) { left = 0; right = 0; }

    gconfirm.box[0].style.left = left + "px";
    if (-1 !== right) gconfirm.box[0].style.right = 0;
    else gconfirm.box[0].style.right = "unset";

    gconfirm.box[0].style.top = top + "px";

    //trace("  --- left: ", left);
    //trace("  --- top: ", top);

    gconfirm.box.fadeIn(100, function () {
        gconfirm.opened = true;

        var winWidth = document.documentElement.clientWidth;
        if ((gconfirm.box[0].offsetLeft + width) > winWidth) {
            gconfirm.box[0].style.left = "0px";
            gconfirm.box[0].style.right = "0px";
        }

        if ("undefined" !== typeof autoCloseTimeout && null !== autoCloseTimeout && 0 < autoCloseTimeout) {
            setTimeout(function () { gconfirm.hide(); }, autoCloseTimeout);
        }
    });
};

gconfirm.init = function ($, n) {
    // attaches event to body, that closes alert on any click
    // creates [gconfirm] element in body
    if ("undefined" === typeof n || null === n) { n = 1; } else { n = parseInt(n, 0); if (isNaN(n)) n = 1; }
    //trace(".gconfirm.init() 10: try n:"+n);
    if (20 < n) return;
    if ("undefined" === typeof $ || null === $) {
        setTimeout(function () { gconfirm.init($, n + 1); }, 100);
        return;
    }
    if ("function" !== typeof $.fn.addBindInfo) {
        setTimeout(function () { gconfirm.init($, n + 1); }, 100);
        return;
    }
    var body = $("body");
    if (1 > body.length) {
        setTimeout(function () { gconfirm.init($, n + 1); }, 100);
        return;
    }
    //trace(".gconfirm.init() 90:");
    gconfirm.get();
    if (null === gconfirm.box) return;
    //gconfirm.box.off().on("click", function () { gconfirm.hide(); }).addBindInfo("click: gconfirm.hide()");
    trace(".gconfirm.init() 100:");
    body.click(function (e) { gconfirm.hide(); }).addBindInfo("click: .gconfirm.hide 781:");
};
// <-- gconfirm

function stopPropagation(e) {
    if ("undefined" === typeof (e) || null === e) return;
    try {
        if (typeof e.stopPropagation === "function") e.stopPropagation();
        else e.cancelBubble = true;
        e.preventDefault();
    }
    catch (e) { ; }
}

function setDefaultProjectImage(o) { setDefaultImage(o, '/images/default_project.png'); }

function setDefaultProjectImageGray(o) { setDefaultImage(o, '/images/project_gr.png'); }

function setDefaultProjectIcon(o) { setDefaultImage(o, '/images/project_icon_gr.png'); }

function setDefaultAvatarImage(o) { setDefaultImage(o, '/images/default-avatar.svg'); }

function setDefaultIconImage(o) { setDefaultImage(o, '/images/default-icon.png'); }

function setDefaultAppIconImage(o) { setDefaultImage(o, '/images/default-app-icon.png'); }

function setDefaultScreenshotWeb(o) { setDefaultImage(o, '/images/default-scr-web-w.jpg'); }

function setDefaultScreenshotAndroid(o) { setDefaultImage(o, '/images/default-scr-and-w.jpg'); }

function setDefaultScreenshotIos(o) { setDefaultImage(o, '/images/default-scr-ios-w.jpg'); }

function setDefaultFeatureIcon(o) { setDefaultImage(o, '/images/feature_icon_blk.png'); }

function setDefaultImage(o, src) {
    if ("undefined" === typeof o || null === o) return;
    o.onerror = null;
    o.src = src;
}

function fixHTMLForMessage(s) {
    if ("undefined" === typeof s || null === s || s === "") return "";
    s = s.toString().trim();
    if (s === "") return "";

    s = s.replace("<!DOCTYPE html>", "").replace("<!doctype html>", "")
        .replace(/<html/ig, "<div class='q-html'").replace(/\/html>/ig, "/div>")
        .replace(/<head/ig, "<div class='q-head' style='margin:0;padding:0;'").replace(/\/head>/ig, "/div>")
        .replace(/<body/ig, "<div class='q-body' style='margin:0;padding:0;'").replace(/\/body>/ig, "/div>")
        .replace(/<header/ig, "<div class='q-header' style='margin:0;padding:0;'").replace(/\/header>/ig, "/div>")
        .replace(/<footer/ig, "<div class='q-footer' style='margin:0;padding:0;'").replace(/\/footer>/ig, "/div>")
        .replace(/<script/ig, "<div style='display:none;'").replace(/\/script>/ig, "/div>")
        .replace("body{", ".q-body{").replace("body {", ".q-body{")
        .replace("a:link", ".q-body a:link").replace(",a", ",.q-body a").replace("a:hover", ".q-body a:hover")
        .replace("h1{", ".q-body h1{").replace("h2{", ".q-body h2{").replace("h3{", ".q-body h3{text-align:center;").replace("h4{", ".q-body h4{")
        .replace("code{", ".q-body code{")
        .replace("pre{", ".q-body pre{")
        .replace("ul,ol{", ".q-body ul,.q-body ol{")
        .replace("#header", ".q-body #header")
        .replace("#content{margin:0 0 0 2%;", ".q-body #content{margin:0;")
        .replace(",.content-container{", ",.q-body .content-container{text-align:left !important;width:100% !important;margin:0 !important;")
        .replace("\n.content-container", "\n.q-body .content-container")
        .replace("table{", ".q-body table{")
        .replace("td,th{", ".q-body td,.q-body th{")
        .replace("\ntable tr", "\n.q-body table tr")
        .replace(",table tr", ",.q-body table tr")
        .replace("\nth{", "\n.q-body th{")
        .replace("\nthead th{", "\n.q-body thead th{")
        .replace("\n.clear{", "\n.q-body .clear{")
        .replace("\nul.first,ol.first{", "\n.q-body ul.first,.q-body ol.first{")
        .replace("\nfieldset{", "\n.q-body fieldset{")
        .replace("\nlegend", "\n.q-body legend")
        .replace("font-size:.7em", "font-size:12px")
        .replace("\n.preferred", "\n.q-body .preferred")
        .replace("\n.highlight-code", "\n.q-body .highlight-code")
        .replace("\n.summary-container", "\n.q-body .summary-container")
        .replace(/container/g, "cntainer")
        .replace(/cntent/g, "cntent")
        .replace("margin: 20vh 0", "")
        .replace("href=", " href='javascript:void(0);' q-href=")
        .replace("onclick=", "q-onclick=");

    return s;
}

function jqXHR_getSafeErrorHTMLResponseText(jqXHR, mode) {
    // jqXHR - jquery XHR obj
    // mode:
    // - html (default) : try to modify ALL content
    // - text           : wrap content with <xmp> ... </xmp>
    // - body           : create DOM, get & modify only BODY
    
    var sp = "head.jqXHR_getSafeErrorHTMLResponseText()";

    if ("undefined" === typeof jqXHR || null === jqXHR) return "";
    if ("undefined" === typeof mode || null === mode) mode = "html";
    var _s = "";

    //trace("head.jqXHR_getSafeErrorHTMLResponseText() 10: typeof jqXHR: ", typeof jqXHR);
    //trace(sp + " 10: mode: ", mode);
    //trace(sp + " 10: jqXHR: ", jqXHR);

    var responseType = "";
    var status = 0;

    if ("string" === typeof jqXHR) _s = jqXHR;
    else {

        if ("undefined" === typeof jqXHR.responseType) {
            if ("string" !== typeof jqXHR.responseText && "string" !== typeof jqXHR.responseJSON) return "";
            if ("string" === typeof jqXHR.responseJSON && null !== jqXHR.responseJSON && "" !== jqXHR.responseJSON) responseType = "json";
            else responseType = "text";
        }
        else responseType = jqXHR.responseType;

        if ("number" === typeof jqXHR.status) status = jqXHR.status;

        switch (responseType) {
            case '':
            case 'text':
                if ("string" === typeof jqXHR.responseText) {
                    try { _s = jqXHR.responseText.toString().trim(); }
                    catch (e) {
                        return "[ " + sp + " parser error 20:] failed to parse server response ! [ responseType: " + responseType + " ]";
                    }
                }
                else _s = "Status [" + status + "]. Network/Server error. [ responseType: " + responseType + "] ";
                break;

            case 'json':
                if ("string" === typeof jqXHR.responseJSON) _s = jqXHR.responseJSON;
                _s = "Status [" + status + "]. Network/Server error. [ responseType: " + responseType + "] " + _s;
        }
    }
    if (_s.length < 1) return "";

    var _s1 = "";
    var x = _s.indexOf("<!DOCTYPE html>");
    if (x > 0) {
        _s1 = _s.substr(0, x) + "<hr />";
        _s = _s.substring(x);
    }

    switch (mode) {
        case "text": return "<xmp>\n" + _s + "\n</xmp>";
        case "body":
            try {
                trace(sp + " 100: Try to use DOMParser()");
                var dom = new DOMParser().parseFromString(_s, 'text/html');
                var jq = $(dom).find("body");
                jq.find("header").remove();
                jq.find("footer").remove();
                jq.find("script").remove();
                jq.find("link").remove();
                _s = jq.html();
            }
            catch (e) { trace(sp + " 190: ERR: -- dom parser failed. return as text! e: ", e, "error"); }
            break;

        case "html":
            _s = _s.removeHTMLTags("header").removeHTMLTags("footer").removeHTMLTags("script").removeHTMLTags("link").replaceHTMLTag("pre", "div");//, " class='pre-div'");
            break;
    }
    //_s = "\n" + fixHTMLForMessage(_s1 + _s) + "\n";
    //trace(sp + " end: _s: ", _s);
    return "\n" + fixHTMLForMessage(_s1 + _s) + "\n";
}

var gscreenshot = gscreenshot || {};
gscreenshot.ready = false;
gscreenshot.storage = null;
gscreenshot.init = function (callbackOK, callbackFail) {
    try {
        if (gscreenshot.ready) {
            if ("function" === callbackOK) callbackOK();
            return;
        }
        gscreenshot.storage = document.getElementById("gscreenshots");
        if (null === gscreenshot.storage) {
            gscreenshot.storage = document.createElement("DIV");
            gscreenshot.storage.id = "gscreenshots";
            gscreenshot.storage.style.display = "none";
            document.body.appendChild(gscreenshot.storage);
        }
        $.when(
            $.getScript('/scripts/html2canvas.min.js'),
            $.Deferred(function (deferred) {
                $(deferred.resolve);
            })
        ).done(function () {
            trace("gscreenshot.init() 500: .success() js loaded.");
            gscreenshot.ready = true;
            if ("function" === typeof callbackOK) { callbackOK(); }
        }).fail(function () {
            trace("gscreenshot.init()", "600: .fail() -- js load failed.", "error");
            if ("function" === typeof callbackFail) { callbackFail(); }
        });
    }
    catch (e) {
        $('#feedback-tab').removeClass("opened");
        trace("gscreenshot.init() 900: err:", e, "error");
    }
    return;
};
gscreenshot.addToStorage = function (canvas) {
    try {
        if (null !== gscreenshot.storage) gscreenshot.storage.appendChild(canvas);
    }
    catch (e) {
        trace("gscreenshot.addToStorage() 100: err:", e, "error");
    }
};


gscreenshot.restorePageAfterScreenCopy = function () {
    let $intro = $("#main > section.main__content > section.intro");
    if (1 > $intro) return;
    $intro.removeClass("noimg");
};

gscreenshot.preparePageBeforeScreenCopy = function () {
    let $intro = $("#main > section.main__content > section.intro");
    if (1 > $intro) return;

    $intro.addClass("noimg");// temp. remove animated SVG background

    // restore page after screencopy
    setTimeout(function () {
        gscreenshot.restorePageAfterScreenCopy();
    }, 30000);
};

gscreenshot.create = function (callback) {
    try {

        // ! prepare is required cause html2canvas can fail
        gscreenshot.preparePageBeforeScreenCopy();

        if (!gscreenshot.ready) {
            gscreenshot.init(function () { gscreenshot.get(callback); });
            return;
        }
        //let htmlElm = document.body;//("html");
        let htmlElm = document.body;
        if (null === htmlElm) {
            trace("gscreenshot.create()"," 20: err: not found html element.", "error");
            return;
        }
        //trace("gscreenshot.create() 50: htmlElm: ", htmlElm);

        html2canvas(htmlElm).then(
            canvas => {
                try {
                    canvas.id = "sc-" + (new Date()).getTime().toString();
                    gscreenshot.addToStorage(canvas);

                    if ("function" === typeof callback) {
                        canvas.toBlob(callback);
                        //callback(canvas);
                    }
                    gscreenshot.restorePageAfterScreenCopy();
                    return canvas;
                }
                catch (e) {
                    trace("gscreenshot.create() 100: html2canvas fail. err:", e, "error");
                    gscreenshot.restorePageAfterScreenCopy();
                    if ("function" === typeof callback) callback();
                    return null;
                }
            },
            error => {
                trace("gscreenshot.create() 200: html2canvas fail. err:", error, "error");
                gscreenshot.restorePageAfterScreenCopy();
                if ("function" === typeof callback) callback();
            }
        );
    }
    catch (e) {
        trace("gscreenshot.create() 300: html2canvas fail. err:", e, "error");
        gscreenshot.restorePageAfterScreenCopy();
        if ("function" === typeof callback) callback();
    }
};
gscreenshot.getLast = function () {
    try {
        if (null === gscreenshot.storage) return null;
        return gscreenshot.storage.lastChild;
    }
    catch (e) {
        trace("gscreenshot.getLast() 100: err:", e, "error");
    }
};

function traceShowViews() {
    if ("undefined" === typeof window.showDebugInfo || !window.showDebugInfo) return;
    // show in console ALL View Names found on page
    var views = new Array();
    $("*").contents().filter(function () {
        return this.nodeType === 8 && this.nodeValue.indexOf("::vw::")>-1;
    }).each(function (index, element) {
        views.push(element.nodeValue);
    });

    trace("=========== head.traceShowViews() [::vw::] views:", views);
}

function replaceDataPriceToLocal() {
    trace("head.replaceDataPriceToLocal()");

    $("*[data-rprice]").each(function () {
        var v = this.getAttribute("data-rprice");
        var addCurrency = true;
        var marker = "nocurrency:";
        if (typeof v === "string" && v.indexOf(marker) > -1){
            addCurrency = false;
            v = v.substr(marker.length);
        }
        this.innerHTML = toLocalMoney(v, "Free", true, "$", addCurrency, true);
    });
}

function mytmpl_onVideoThumbnailError(o) {
    trace("head.mytmpl_onVideoThumbnailError()");
    if ("undefined" === typeof o || null === o) return;
    var videoBox = $(o).parents(".video-thumbnail");
    if (1 > videoBox.length) {
        trace("head.mytmpl_onVideoThumbnailError 100: err: Not found parent [.video-thumbnail]");
        return;
    }
    videoBox[0].style.backgroundImage = "url('/images/video-2-gray-1.png')";//"url('/images/video-1-black-o10.svg')";
    videoBox[0].style.backgroundSize = "auto 75%";
}

ghead.init = function ($, n) {
    if ("undefined" === typeof n || null === n) { n = 1; } else { n = parseInt(n, 0); if (isNaN(n)) n = 1; }
    if (20 < n) return;
    var _ = ghead;
    if ("undefined" === typeof $ || null === $) {
        setTimeout(function () { _.init($, n + 1); }, 100);
        return;
    }
    trace("ghead.init() sdengine:", sdengine);

    gmouse.init($, 1);// mouse position
    gconfirm.init($, 1);// confirmation popup
    galert.init($, 1);// alert popup

    $(document).ready(function ($) {
        traceShowViews($);
        replaceDataPriceToLocal();
        //ghead.SVG.svgInline();

        //setTimeout(function () { test123(); }, 1000);
    });
};

function test123() {
    trace("====== test123()");

    var o = document.createElement("DIV");
    o.id = "test123";
    o.innerHTML = jqXHR_getSafeErrorHTMLResponseText(TestServerErrorResponse, "html");
    document.body.appendChild(o);
}

var TestServerErrorResponse = "Could not upload image Could not upload file icon.png, Server returned NotFound Not Found | <!DOCTYPE html>"
    + "<html><head>"
    + "\n<meta charset=\"utf-8\" /><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
    + "\n<link rel=\"shortcut icon\" href=\"/images/favicon/favicon.ico\" type=\"image/x-icon\"><link rel=\"icon\" href=\"/images/favicon/favicon.ico\" type=\"image/x-icon\">"
    + "\n<link rel=\"icon\" type=\"image/png\" href=\"/images/favicon/favicon-32x32.png\" sizes=\"32x32\" /><link rel=\"icon\" type=\"image/png\" href=\"/images/favicon/favicon-16x16.png\" sizes=\"16x16\" />"
    + "\n<title>App.net ID</title>"
    + "\n<meta name=\"keywords\" content=\"App.net ID\" /><meta name=\"description\" content=\"Manage your App.net account\" />"
    + "\n<link href=\"/Content/bootstrap.css\" rel=\"stylesheet\" /><link href=\"/Content/fonts.css\" rel=\"stylesheet\" /><link href=\"/Content/font-awesome.min.css\" rel=\"stylesheet\" /><link href=\"/Content/animate.css\" rel=\"stylesheet\" /><link href=\"/Content/site.css\" rel=\"stylesheet\" />"
    + "\n<script src=\"/Scripts/jquery-1.10.2.js\"></script>"
    + "\n</head>"
    + "\n<body>"
    + "\n<header><div class=\"container header-container\"><div class=\"navbar-header\" role=\"navigation\"><a class=\"navbar-brand\" href=\"/\"><img src=\"/images/logo.svg\" /></a></div></div></header>"
    + "\n<div class=\"container-fluid body-content\">"
    + "\n<div class=\"container text-center\" style=\"margin: 20vh 0\"><hgroup><h1>404</h1><h3>Woops. Looks like this page doesn't exist.</h3></hgroup><h6><a href=\"/\">Go Back To Home Page</a></h6></div>"
    + "\n<table id='qqqtest123' width=\"100%\" bgcolor=\"#ffffcc\"><tbody><tr><td>"
    + "\n<code>"
    + "\n   <pre class='pre-test-123'>"
    + "\n    WRN: Assembly binding logging is turned OFF. To enable assembly bind failure logging, set the registry value [HKLM\\Software\\Microsoft\\Fusion!EnableLog] (DWORD) to 1. Note: There is some performance penalty associated with assembly bind failure logging. To turn this feature off, remove the registry value [HKLM\\Software\\Microsoft\\Fusion!EnableLog].\n"
    + "\n   </pre>"
    + "\n</code>"
    + "\n</td></tr></tbody></table>"
    + "\n</div>"
    + "\n<footer><div class=\"container\">"
    + "\n<div class=\"row\"><div class=\"col-sm-6\"><p>Copyright &copy; 2019 <a href=\"http://mobiblocks.com\">MOBIBLOCKS</a>. All rights reserved.</p></div><div class=\"col-sm-6 text-right legal-links\">"
    + "\n<a href=\"/privacy\">Privacy Policy</a><a href=\"/terms\">Terms of Use</a></div></div>"
    + "\n</div></footer>"
    + "\n<script src=\"/Scripts/jquery-1.10.2.js\"></script><script src=\"/Scripts/bootstrap.js\"></script><script src=\"/Scripts/respond.js\"></script>"
    + "\n</body></html>";

ghead.init($);

try { console.log("<-- head.js"); } catch (e) { ; };
