
window.loadingEnabled = false;

window.showLoading = function (visible) {
    if (!window.loadingEnabled) return;
    var id = "global-loading";
    var div = document.getElementById(id);
    if (!div) {
        var html = "<div>";
        for (var i = 0; i < 8; i++) html += '<span></span>';
        html += '</div>'
        div = document.createElement('div');
        div.innerHTML = html;
        div.id = id;
        div.className = id;
        div.style.display = 'none';
        document.body.appendChild(div);
    }
    div.style.display = visible ? 'block' : 'none';
};

window.uuid = function (hyphenation) {
    var array = [];
    var hex = "0123456789abcdef";
    for (var i = 0; i < 36; i++) array[i] = hex.substr(Math.floor(Math.random() * 16), 1);
    array[14] = "4";
    array[19] = hex.substr((array[19] & 0x3) | 0x8, 1);
    array[8] = array[13] = array[18] = array[23] = "-";
    var result = array.join("");
    if (!hyphenation) result = result.replace(/-/gm, "");
    return result;
};

window.isFloat = function (number) {
    if (typeof number != "number") return false;
    if (isNaN(number)) return false;
    if (!isFinite(number)) return false;
    return true;
};

window.toFloat = function (number) {
    var input = number;
    var hasPercent = false;
    if (typeof input == "string") {
        input = input.replace(/,/gm, "").replace(/\ /gm, "");
        if (input.indexOf("%") > -1) {
            hasPercent = true;
            input = input.replace(/%/gm, "");
        }
    }
    var value = parseFloat(input);
    if (isNaN(value)) return 0.0;
    if (!isFinite(value)) return 0.0;
    if (hasPercent) value = value / 100.0;
    return value;
};

window.toPercent = function (number, digitals) {
    var input = toFloat(number);
    // var input = number;
    // if (typeof input != "number") {
    //     input = parseFloat(input);
    //     if (isNaN(input)) return "invalid";
    //     if (isFinite(input)) return "finite";
    // };
    var value = input * 100.0;
    if (typeof digitals == "number" && digitals > 0) value = value.toFixed(digitals);
    var text = value + "%";
    return text;
};

/**
 * @function 将 number 转为金额格式的文本，参数 digitals 表示小数位数。
 * @param number {Number} 要转换的数值。
 * @param digitals {Number} 金额的小数位数，不指定时默认为 2 位。
 */
window.toPrice = function (number, digitals, thousands, point) {
    if (typeof number == "number") number = number.toFixed(2);
    if (typeof number != "string") return "invalid";
    number = parseFloat(number);
    number = (number + '').replace(/[^0-9+-Ee.]/g, '');
    var n = !isFinite(+number) ? 0 : +number,
        prec = !isFinite(+digitals) ? 2 : Math.abs(digitals),
        sep = (typeof point == thousands) ? thousands : ",",
        dec = (typeof point == "string") ? point : '.',
        s = '',
        toFixedFix = function (n, prec) {
            var k = Math.pow(10, prec);
            return '' + Math.ceil(n * k) / k;
        };
    s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
    var re = /(-?\d+)(\d{3})/;
    while (re.test(s[0])) {
        s[0] = s[0].replace(re, "$1" + sep + "$2");
    };
    if ((s[1] || '').length < prec) {
        s[1] = s[1] || '';
        s[1] += new Array(prec - s[1].length + 1).join('0');
    };
    return s.join(dec);
};

// 对数组排序
window.sort = function (array, comparison) {
    if (typeof Array.isArray === 'function') {
        var isArray = Array.isArray(array);
        if (!isArray) return array;
    } else {
        var isArray = Object.prototype.toString.call(array) === '[object Array]';
        if (!isArray) return array;
    }
    if (typeof comparison != 'function') return array;
    function swap(array, i, j) {
        var temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    if (!comparison || typeof comparison != 'function') return array
    for (var i = 0; i < array.length - 1; i++) {
        for (var j = i + 1; j < array.length; j++) {
            var compared = comparison(array[i], array[j]);
            if (compared > 0) swap(array, i, j);
        }
    }
    return array;
};

// 显示吐司提示。
// _html：要显示的内容。
// _duration：持续时间，单位毫秒，不指定时默认 2000 毫秒。
window.toast = function (_html, _duration) {
    if (document.getElementsByClassName) {
        var shown = document.getElementsByClassName("elivo.toast");
        for (i = 0; i < shown.length; i++) {
            try {
                document.body.removeChild(shown[i]);
            } catch (error) { };
        };
    };
    if (typeof _html != "string" || _html == "") return;

    // 内容较短时，去除末尾的句号。
    if (_html.length < 20) {
        if (_html.length > 0 && _html.substring(_html.length - 1) == "。") _html = _html.substring(0, _html.length - 1);
    }
    if (!_html) return;

    var box = document.createElement("span");
    var area = document.createElement("div");
    console.info("elivo.toast", _html);
    box.style.backdropFilter = "blur(5px)";
    box.style.backgroundColor = "#3f3f3f";
    box.style.borderRadius = "5px";
    box.style.boxShadow = "0px 10px 40px rgba(0,0,0, 0.3)";
    box.style.color = "white";
    box.style.display = "inline-block";
    box.style.fontFamily = "consolas, 'microsoft yahei', 'pingfang sc', sans-serif";
    box.style.fontSize = "14px";
    box.style.lineHeight = "28px";
    box.style.paddingBottom = "10px";
    box.style.paddingLeft = "20px";
    box.style.paddingRight = "20px";
    box.style.paddingTop = "10px";
    box.style.textShadow = "0px 0px 10px rgba(0,0,0, 0.5)";
    box.innerHTML = _html;
    area.className = "elivo.toast";
    area.style.bottom = "20%";
    area.style.display = "block";
    area.style.fontSize = "0px";
    area.style.left = "10%";
    area.style.opacity = "0";
    area.style.position = "fixed";
    area.style.right = "10%";
    area.style.textAlign = "center";
    area.style.transition = "all 0.15s linear";
    area.style.zIndex = "200000000";
    area.appendChild(box);
    document.body.appendChild(area);
    setTimeout(function () {
        area.style.opacity = 1;
        var delay = 2000;
        if (typeof _duration == "number") {
            delay = parseInt(_duration);
            if (delay == -1) return;
        }
        delay = 150 + delay;
        setTimeout(function () {
            area.style.opacity = 0;
            setTimeout(function () {
                try {
                    document.body.removeChild(area);
                } catch (error) { };
            }, 300);
        }, delay);
    }, 1);
    return { area: area, box: box };
};

// ajax
(function () {

    function _receive(xhr, succeed, failed) {
        xhr.onreadystatechange = () => {
            if (xhr.readyState != 4) return
            var response = xhr.responseText;
            try {
                var json = JSON.parse(response);
                response = json;
            } catch (err) {
                // console.error("ajax", err.message);
            }
            if (xhr.status == 200 || xhr.status == 304) {
                if (typeof succeed === "function") succeed(response);
            } else {
                if (typeof failed === "function") failed(response);
            }
        }
    }

    window.ajaxGet = function (url, succeed, failed) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        _receive(xhr, succeed, failed);
        try {
            xhr.send();
        } catch (err) {
            // console.error(err);
            if (typeof failed === "function") failed(err.message);
        }
    }

    window.ajaxPost = function (url, data, succeed, failed) {
        var xhr = new XMLHttpRequest()
        var content = "";
        if (data) {
            if (typeof data == "string") content = data;
            else if (typeof data == "object") content = data;
            else content = String(data);
        }
        xhr.open("POST", url, true)
        xhr.setRequestHeader("Content-Type", "text/plain")
        _receive(xhr, succeed, failed);
        try {
            xhr.send(content);
        } catch (err) {
            // console.error(err);
            if (typeof failed === "function") failed(err.message);
        }
    }

    // 使用 AJAX 上传文件。
    window.ajaxUpload = function (url, succeed, failed, accept, before, after, progress, _blob) {
        if (typeof succeed != "function") succeed = function () { };
        if (typeof failed != "function") failed = function (msg) { console.error("ajaxUpload", msg); };
        if (typeof before != "function") before = function (file) { console.log("ajaxUpload", "before", file); };
        if (typeof after != "function") after = function (file) { console.log("ajaxUpload", "after", file); };
        if (typeof progress != "function") progress = function (p) { console.log("ajaxUpload", "progress", p); };
        if (!url) return failed("未指定 URL。");

        var input = document.createElement("input");
        input.type = "file";
        if (accept && typeof accept == "string") input.accept = accept;
        input.click();
        input.onchange = function (evt) {
            if (evt.target.files.length < 1) return failed("没有选择文件。");

            if (typeof url == "function") url = url(evt.target.files[0]);
            if (typeof url != "string") return failed("未获取 URL。");

            var file = evt.target.files[0];
            if (typeof before == "function") {
                var value = before(file);
                if (typeof value == "boolean" && value == false) return;
                if (typeof value == "number" && value == 0) return;
            }

            var reader = new FileReader();
            reader.file = file;
            reader.onabort = function (e) { };
            reader.onerror = function (e) { };
            reader.onloadstart = function (e) { };
            reader.onprogress = function (e) { };
            reader.onload = function (e) { };
            reader.onloadend = function (e) {
                var xhr = new XMLHttpRequest();
                xhr.open("post", url);
                xhr.setRequestHeader("Content-Type", "application/octet-stream");
                if (_blob) xhr.responseType = 'arraybuffer';
                xhr.onreadystatechange = function (e) {
                    if (xhr.readyState == 4) {
                        after(file);
                        if (xhr.status == 200) {
                            if (_blob) {
                                var blob = new Blob([xhr.response]);
                                if (typeof _succeed === 'function') _succeed(blob);
                                return;
                            }
                            var response = xhr.responseText;
                            try { response = JSON.parse(response); } catch (err) { }
                            succeed(response);
                        } else failed("Status=" + String(xhr.status));
                    }
                };
                xhr.onprogress = function (e) {
                    if (e.lengthComputable) {
                        var p = { name: reader.file.name, total: e.total, loaded: e.loaded };
                        progress(p);
                    }
                };
                xhr.send(reader.result);
            };
            reader.readAsArrayBuffer(file);
        };
    }

})();

// 从指定字符串中获取参数。
window.urlQuery = function (selector, decode) {
    var combined = window.location.search;
    if (!combined) return "";
    switch (typeof selector) {
        case "string":
        case "number":
            break;
        default:
            return "";
    };
    var matched = combined.match(new RegExp("[\?\&#][^\?\&]+=[^\?\&]+", "g"));
    var array = [];
    if (matched) {
        for (var i = 0; i < matched.length; i++) {
            var split = matched[i].substring(1).split("=");
            array.push({ key: split[0], value: split[1] });
        };
    };
    var value = "";
    switch (typeof selector) {
        case "string":
            for (var i in array) {
                if (array[i].key == selector) {
                    value = array[i].value;
                    break;
                };
            };
            break;
        case "number":
            var i = parseInt(selector);
            if (i >= 0 && i < array.length) {
                value = array[i].value;
                break;
            };
            break;
    };
    if (decode) value = decodeURIComponent(value);
    return value;
}

window.toLucid = window.lucidDate = function (date) {
    if (typeof date === 'number' || typeof date === 'string') return lucidDate(new Date(date))
    if (date && date instanceof Date) {
        var year = date.getFullYear()
        var month = date.getMonth() + 1
        var day = date.getDate()
        var hour = date.getHours()
        var minute = date.getMinutes()
        var second = date.getSeconds()
        var milli = date.getMilliseconds()
        year = (year < 10 ? '000' : (year < 100 ? '00' : (year < 1000 ? '0' : ''))) + String(year)
        month = (month < 10 ? '0' : '') + String(month)
        day = (day < 10 ? '0' : '') + String(day)
        hour = (hour < 10 ? '0' : '') + String(hour)
        minute = (minute < 10 ? '0' : '') + String(minute)
        second = (second < 10 ? '0' : '') + String(second)
        milli = (milli < 10 ? '00' : (milli < 100 ? '0' : '')) + String(milli)
        return year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second //  + '.' + milli
    }
    if (date && date._d instanceof Date) return lucidDate(date._d)
    return ''
}

// 从指定字符串中获取参数。
window.urlHash = function (selector, decode) {
    var combined = window.location.hash;
    if (!combined) return "";
    switch (typeof selector) {
        case "string":
        case "number":
            break;
        default:
            return "";
    };
    var matched = combined.match(new RegExp("[\?\&#][^\?\&]+=[^\?\&]+", "g"));
    var array = [];
    if (matched) {
        for (var i = 0; i < matched.length; i++) {
            var split = matched[i].substring(1).split("=");
            array.push({ key: split[0], value: split[1] });
        };
    };
    var value = "";
    switch (typeof selector) {
        case "string":
            for (var i in array) {
                if (array[i].key == selector) {
                    value = array[i].value;
                    break;
                };
            };
            break;
        case "number":
            var i = parseInt(selector);
            if (i >= 0 && i < array.length) {
                value = array[i].value;
                break;
            };
            break;
    };
    if (decode) value = decodeURIComponent(value);
    return value;
};

// 获取查询字符串中的返回地址。
function getReturn() {
    var ret = urlQuery("back");
    if (!ret) ret = urlQuery("return");
    if (!ret) ret = "/";
    return ret;
};

// api
(function () {

    var _timeout = 10000;

    function uuid(hyphenation) {
        var array = [];
        var hex = "0123456789abcdef";
        for (var i = 0; i < 36; i++) array[i] = hex.substr(Math.floor(Math.random() * 16), 1);
        array[14] = "4";
        array[19] = hex.substr((array[19] & 0x3) | 0x8, 1);
        array[8] = array[13] = array[18] = array[23] = "-";
        var result = array.join("");
        if (hyphenation !== true) result = result.replace(/-/gm, "");
        return result;
    }

    function getLocalStorage(_key) {
        var value = localStorage.getItem(_key);
        if (typeof value == "string") {
            if (value.length < 1) {
                localStorage.removeItem(_key);
                value = null;
            }
        }
        return value;
    }

    function setLocalStorage(_key, _value) {
        var value = _value;
        if (typeof value == "string") {
            if (value.length < 1) value = null;
        } else value = null;
        if (value == null) {
            var current = localStorage.getItem(_key);
            if (typeof current == "string") localStorage.removeItem(_key);
        } else localStorage.setItem(_key, value);
    }

    function getSession() {
        var session = getLocalStorage("Account.Session");
        if (session && typeof session == "string") return session;
        session = uuid(false);
        setLocalStorage("Account.Session", session);
        return session;
    }

    function getTicket() {
        var ticket = getLocalStorage("Account.Ticket");
        if (!ticket) {
            ticket = getLocalStorage("Access-Token");
            if (ticket) ticket = ticket.replace(/\"/gm, "");
        }
        return ticket;
    }

    function setTicket(value) {
        return setLocalStorage("Account.Ticket", value);
    }

    function getUrl(path) {
        if (!path) return "";
        if (path.substring(0, 1) != "/") path = "/" + path;
        return path;
    }

    function invoke(_function, _json) {
        showLoading(false);
        if (_json) {
            if (_json.status != "ok") {
                console.error("api", _json.status, _json.message, _json, _json.xhr);
                // if (typeof toast == "function") toast(_json.message, 3000);
            }
        }
        if (typeof _function == "function") _function(_json);
    }

    function onFailed(_failed, text) {
        if (typeof _failed != "function") return;
        if (typeof text != "string") text = "发生了未知错误。";
        if (typeof !text) text = "发生了未知错误。";
        _failed({ status: "failed", text });
    }

    function _get(_url, _data, _succeed, _failed, timeout) {
        showLoading(true);
        var url = getUrl(_url);
        var first = true;
        if (_data) {
            for (var i in _data) {
                url = url + (first ? "?" : "&");
                url = url + encodeURIComponent(i);
                url = url + "=";
                url = url + encodeURIComponent(_data[i]);
                first = false;
            }
        }
        var ticket = getTicket();
        if (ticket) {
            url = url + (first ? "?" : "&");
            url += "ticket=" + ticket;
        }

        if (window.XDomainRequest) {
            var xdr = new XDomainRequest();
            xdr.onerror = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            };
            xdr.ontimeout = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Timeout", xdr: xdr });
            };
            xdr.onprogress = function () { };
            xdr.onload = function () {
                var response = xdr.responseText;
                var json = null;
                try { json = JSON.parse(response); } catch (ex) { invoke(_failed, { status: "error", message: ex.message, xdr: xdr }); }
                if (json) {
                    if (json.status == "unauthorized") {
                        return api.account.gotoLogin()
                    }
                    else {
                        return invoke(json.status == "ok" ? _succeed : _failed, json);
                    }
                }
                invoke(_failed, { status: "text", message: "Invalid Response", text: response, xdr: xdr });
            };
            if (timeout) xdr.timeout = timeout;
            else if (_timeout) xdr.timeout = _timeout;
            try {
                xdr.open("get", url);
                xdr.send();
            } catch (ex) {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            }
            return xdr;
        }
        var result = {
            then: function (callback) { },
            catch: function (callback) { }
        };
        var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open("GET", url, true);
        if (timeout) xhr.timeout = timeout;
        else if (_timeout) xhr.timeout = _timeout;
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if ((xhr.status == 200) || (xhr.status == 304)) {
                    var response = xhr.responseText;
                    var json = null;
                    try { json = JSON.parse(response); } catch (ex) { invoke(_failed, { status: "error", message: ex.message, xhr: xhr }); }
                    if (json) {
                        if (json.status == "unauthorized") return api.account.gotoLogin();
                        else return invoke(json.status == "ok" ? _succeed : _failed, json);
                    }
                    invoke(_failed, { status: "text", message: "Invalid Response", text: response, xhr: xhr });
                } else {
                    invoke(_failed, { status: "error", message: "Invalid Status " + String(xhr.status), xhr: xhr });
                }
            }
        };
        try { xhr.send(); } catch (ex) { invoke(_failed, { status: "error", message: ex.message, xhr: xhr }); }
        return result
    }

    function _post(_url, _data, _succeed, _failed, timeout) {
        showLoading(true);
        var content = {
            "ticket": getTicket(),
            "page": window.location.href,
            "session": getSession(),
            "data": _data
        };
        var url = getUrl(_url);

        if (window.XDomainRequest) {
            var xdr = new XDomainRequest();
            xdr.onerror = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            };
            xdr.ontimeout = function () {
                invoke(_failed, { status: "error", message: "XDomainRequest Timeout", xdr: xdr });
            };
            xdr.onprogress = function () { };
            xdr.onload = function () {
                var response = xdr.responseText;
                var json = null;
                try { json = JSON.parse(response); } catch (ex) { invoke(_failed, { status: "error", message: ex.message, xdr: xdr }); }
                if (json) {
                    if (json.status == "unauthorized") {
                        return api.account.gotoLogin()
                    }
                    else {
                        return invoke(json.status == "ok" ? _succeed : _failed, json);
                    }
                }
                invoke(_failed, { status: "text", message: "Invalid Response", text: response, xdr: xdr });
            };
            if (timeout) xdr.timeout = timeout;
            else if (_timeout) xdr.timeout = _timeout;
            try {
                xdr.open("post", url);
                xdr.send(JSON.stringify(content));
            } catch (ex) {
                invoke(_failed, { status: "error", message: "XDomainRequest Failed", xdr: xdr });
            }
            return xdr;
        }

        var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open("POST", url, true);
        if (timeout) xhr.timeout = timeout;
        else if (_timeout) xhr.timeout = _timeout;
        xhr.setRequestHeader("Content-Type", "text/plain");
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if ((xhr.status == 200) || (xhr.status == 304)) {
                    var response = xhr.responseText;
                    var json = null;
                    try { json = JSON.parse(response); } catch (ex) { invoke(_failed, { status: "error", message: ex.message, xhr: xhr }); }
                    if (json) {
                        if (json.status == "unauthorized") return api.account.gotoLogin();
                        else return invoke(json.status == "ok" ? _succeed : _failed, json);
                    }
                    invoke(_failed, { status: "text", message: "Invalid Response", text: response, xhr: xhr });
                } else {
                    invoke(_failed, { status: "error", message: "Invalid Status " + String(xhr.status), xhr: xhr });
                };
            };
        };
        try { xhr.send(JSON.stringify(content)); } catch (ex) { invoke(_failed, { status: "error", message: ex.message, xhr: xhr }); }
        return xhr;
    }

    window.api = {
        get: function (_url, _data, _succeed, _failed) {
            return _get(_url, _data, _succeed, _failed);
        },
        post: function (_url, _data, _succeed, _failed) {
            return _post(_url, _data, _succeed, _failed);
        },
        url: function () {
            return getUrl();
        },
        account: {
            user: null,

            getTicket: function () { return getTicket() },

            online: function (_yes, _no, _require) {
                return _post("/account/online", null, function (response) {
                    api.account.user = response.data;
                    if (typeof _yes == "function") _yes(response.data);
                }, function (response) {
                    if (typeof _no == "function") _no(response);
                    if (_require) api.account.gotoLogin();
                });
            },

            login: function (_data, _succeed, _failed) {
                var result = _post("/login/login", _data, function (response) {
                    setTicket(response.data.ticket);
                    if (typeof _succeed == "function") {
                        _succeed(response)
                    } else {
                        api.account.gotoIndex()
                    }
                },
                    function (response) {
                        setTicket("");
                        if (typeof _failed == "function") _failed(response);
                        else if (response.message) toast(response.message);
                    })

                return result;
            },

            logout: function (_data) {
                var result = _post("login/logout", _data, function (response) {
                    setTicket("");
                    api.account.gotoLogin()
                },
                    function (response) {
                        setTicket("");
                        api.account.gotoLogin()
                        if (response.message) toast(response.message);
                    })

                return result;

            },

            gotoLogin: function () {
                setTicket("");
                var url = encodeURIComponent(window.location.href);
                url = "/login/?back=" + url;
                window.location.href = url;
            },

            gotoIndex: function () {
                window.location.href = "/";
            }
        }
    }

})();

function saveAs(content, name, bom) {
    if (!content) return;

    var blob;
    if (content instanceof Blob) {
        var blob = content;
        if (bom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
            blob = new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type });
        }
    } else {
        blob = new Blob([content], { type: 'application/octet-stream' });
    }

    var url = (window.URL || window.webkitURL).createObjectURL(blob);
    setTimeout(function () {
        if (typeof url == "string") (window.URL || window.webkitURL).revokeObjectURL(url);
        else file.remove();
    }, 10000);

    // IE 10+
    if (navigator.msSaveOrOpenBlob) return navigator.msSaveOrOpenBlob(blob, name);

    // emulate a tag
    var link = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
    if ("download" in link) {
        link.href = url;
        link.download = name;
        var click = new MouseEvent("click");
        link.dispatchEvent(click);
        return;
    }

    // popup
    if (!window.open(url, "_blank")) window.location.href = url;
}