class RobustEventSource {
    constructor(_url) {
        this._url = _url;
        this._explictClosed = false;
        this._listenerLists = Object.create(null);
        this._tryRecover = this._tryRecover.bind(this);
        this._tryRecover0 = this._tryRecover0.bind(this);
        this._init();
    }
    get url() { return this._backEnd.url; }
    get withCredentials() { return this._backEnd.withCredentials; }
    get onopen() { return this._backEnd.onopen; }
    set onopen(listener) { this._backEnd.onopen = listener; }
    get onmessage() { return this._backEnd.onmessage; }
    set onmessage(listener) { this._backEnd.onmessage = listener; }
    get onerror() { return this._backEnd.onerror; }
    set onerror(listener) { this._backEnd.onerror = listener; }
    get readyState() {
        if (this._backEnd.readyState == 2 && !this._explictClosed)
            return 0;
        return this._backEnd.readyState;
    }
    addEventListener(type, listener, useCapture) {
        this._backEnd['_addEventListener'](type, listener, useCapture);
        var list = this._getListenerList(type, useCapture);
        if (list.indexOf(listener) >= 0)
            list.push(listener);
    }
    dispatchEvent(evt) {
        return this._backEnd.dispatchEvent(evt);
    }
    removeEventListener(type, listener, useCapture) {
        this._backEnd['_removeEventListener'](type, listener, useCapture);
        var list = this._getListenerList(type, useCapture);
        var i = list.indexOf(listener);
        if (i >= 0)
            list.splice(i, 1);
    }
    close() {
        if (!this._explictClosed) {
            this._explictClosed = true;
            this._backEnd['_close']();
            this._backEnd['_removeEventListener']('error', this._tryRecover);
            self.removeEventListener('online', this._tryRecover);
        }
    }
    _init() {
        this._backEnd = new EventSource(this._url);
        ['close', 'addEventListener', 'removeEventListener'].forEach(method => {
            this._backEnd['_' + method] = this._backEnd[method];
            this._backEnd[method] = this[method].bind(this);
        });
        this._backEnd['_addEventListener']('error', this._tryRecover);
        self.addEventListener('online', this._tryRecover);
    }
    _tryRecover(evt) {
        setTimeout(this._tryRecover0, 3000);
    }
    _tryRecover0(evt) {
        if (!this._explictClosed && this._backEnd.readyState == 2) {
            var old = this._backEnd;
            this._init();
            this._backEnd.onerror = old.onerror;
            this._backEnd.onmessage = old.onmessage;
            this._backEnd.onopen = old.onopen;
            ['open', 'message', 'error'].forEach(type => {
                [true, false].forEach(useCapture => {
                    this._getListenerList(type, useCapture).forEach(listener => {
                        this._backEnd.addEventListener(type, listener, useCapture);
                    });
                });
            });
        }
    }
    _getListenerList(type, useCapture) {
        var key = type + ':' + !!useCapture;
        return this._listenerLists[key] || (this._listenerLists[key] = []);
    }
}
var mbe_ui;
(function (mbe_ui) {
    class Domer {
        constructor(options) {
            this._doc = options && options.doc || document;
            this._bookkeeper = options && options.bookkeeper || '_domerBookkeeper';
        }
        render(virtualElem, realElem) {
            return this._render(virtualElem, realElem);
        }
        onBeforeRender(virtualElem, realElem) {
            return virtualElem;
        }
        onAttached(virtualElem, realElem) { }
        onRendered(virtualElem, realElem) { }
        onBeforeRemove(realElem) { }
        inferNamespace(tagName) {
            switch (tagName) {
                case 'svg':
                    return Domer.NS_SVG;
                case 'math':
                    return Domer.NS_MATHML;
                default:
                    return Domer.NS_UNKNOWN;
            }
        }
        _render(vnode, rnode, futureParent) {
            var newNode;
            var currentParent = rnode && rnode.parentNode;
            if (typeof vnode === 'string') {
                if (rnode && rnode.nodeType === Node.TEXT_NODE) {
                    rnode.data = vnode;
                }
                else {
                    newNode = this._doc.createTextNode(vnode);
                }
            }
            else {
                var velem = this.onBeforeRender(vnode, rnode);
                if (velem === false)
                    return rnode;
                if (velem == null) {
                    console.warn('Domer.render: onBeforeRender returned null/undefined, abort!');
                    return null;
                }
                var rname = rnode ? rnode.localName : null;
                var name = (velem.Name === '?' ? rname : velem.Name) || 'div';
                if (!rnode || name !== rname) {
                    var inferredNs = this.inferNamespace(name);
                    var ns;
                    if (typeof inferredNs === 'number') {
                        var parentNode = futureParent || currentParent;
                        ns = parentNode ? parentNode.namespaceURI : this._doc.documentElement.namespaceURI;
                    }
                    else
                        ns = inferredNs;
                    newNode = this._doc.createElementNS(ns, name);
                }
                var targ = (newNode || rnode);
                var fresh = !targ[this._bookkeeper];
                if (!velem.Terminal)
                    this._assignChildren(velem.Kids, targ);
                this._assginProps(velem, targ);
                if (fresh)
                    this.onAttached(velem, targ);
                this.onRendered(velem, targ);
            }
            if (newNode && currentParent) {
                if (rnode.nodeType === Node.ELEMENT_NODE)
                    this.onBeforeRemove(rnode);
                rnode.parentNode.replaceChild(newNode, rnode);
            }
            return newNode || rnode;
        }
        _assginProps(props, realElem) {
            if (!props)
                return;
            var bkn = this._bookkeeper;
            var bookkeeper = realElem[bkn] || (realElem[bkn] = {});
            for (var key in bookkeeper) {
                if (!(bookkeeper[key] !== null && props[key] == null))
                    continue;
                var fc = key.charCodeAt(0) | 0;
                switch (fc) {
                    case PF_ATTR:
                        realElem.removeAttribute(key.slice(1));
                        break;
                    case PF_STYLE:
                        realElem.style.removeProperty(key.slice(1));
                        break;
                    case PF_CLASS:
                        realElem.classList.remove(key.slice(1));
                        break;
                    case PF_EVENT:
                    case PF_EVENT_CAPTURE:
                        replaceEventHandlers(realElem, key.slice(1), bookkeeper[key], undefined, fc === PF_EVENT_CAPTURE);
                        break;
                    default:
                        var attr = webIdl2html[key];
                        if (attr)
                            realElem.removeAttribute(attr);
                        else {
                            realElem[key] = null;
                            if (realElem[key] === 'null')
                                realElem[key] = '';
                        }
                }
                bookkeeper[key] = null;
            }
            for (var key in props) {
                var val = props[key];
                if (val === bookkeeper[key] && !volatileIdl[key] || val == null)
                    continue;
                var fc = key.charCodeAt(0) | 0;
                switch (fc) {
                    case PF_ATTR:
                        realElem.setAttribute(key.slice(1), val);
                        break;
                    case PF_STYLE:
                        realElem.style.setProperty(key.slice(1), val);
                        break;
                    case PF_CLASS:
                        var cl = realElem.classList;
                        if (val)
                            cl.add(key.slice(1));
                        else
                            cl.remove(key.slice(1));
                        break;
                    case PF_EVENT:
                    case PF_EVENT_CAPTURE:
                        replaceEventHandlers(realElem, key.slice(1), bookkeeper[key], val, fc === PF_EVENT_CAPTURE);
                        break;
                    default:
                        realElem[key] = val;
                }
                bookkeeper[key] = val;
            }
        }
        _assignChildren(vKids, realElem) {
            var rKids = realElem.childNodes;
            if (!vKids || !vKids.length) {
                var lc;
                while (lc = realElem.lastChild) {
                    this.onBeforeRemove(lc);
                    realElem.removeChild(lc);
                }
                return;
            }
            for (var i = 0, n = Math.max(vKids.length, rKids.length); i < n; i++) {
                var vKid = vKids[i], rKid = rKids[i];
                if (rKid) {
                    if (vKid == null) {
                        var ns;
                        while (ns = rKid.nextSibling) {
                            this.onBeforeRemove(ns);
                            realElem.removeChild(ns);
                        }
                        realElem.removeChild(rKid);
                        return;
                    }
                    else {
                        this._render(vKid, rKid, realElem);
                    }
                }
                else if (vKid != null) {
                    realElem.appendChild(this._render(vKid, null, realElem));
                }
            }
        }
    }
    Domer.NS_HTML = 'http://www.w3.org/1999/xhtml';
    Domer.NS_SVG = 'http://www.w3.org/2000/svg';
    Domer.NS_MATHML = 'http://www.w3.org/1998/Math/MathML';
    Domer.NS_UNKNOWN = 0;
    mbe_ui.Domer = Domer;
    var PF_ATTR = 64;
    var PF_STYLE = 45;
    var PF_CLASS = 46;
    var PF_EVENT = 42;
    var PF_EVENT_CAPTURE = 33;
    var webIdl2html = { id: 'id', className: 'class', title: 'title', src: 'src', href: 'href', __proto__: null };
    var volatileIdl = { value: true, checked: true, __proto__: null };
    function replaceEventHandlers(realElem, evName, olds, news, capture) {
        if (equalsOrArrayEquals(olds, news))
            return;
        if (Array.isArray(olds)) {
            for (var i = 0, n = olds.length; i < n; i++)
                realElem.removeEventListener(evName, olds[i], capture);
        }
        else if (olds) {
            realElem.removeEventListener(evName, olds, capture);
        }
        if (Array.isArray(news)) {
            for (var i = 0, n = news.length; i < n; i++)
                news[i] && realElem.addEventListener(evName, news[i], capture);
        }
        else if (news) {
            realElem.addEventListener(evName, news, capture);
        }
    }
    function equalsOrArrayEquals(a1, a2) {
        if (a1 === a2)
            return true;
        if (!Array.isArray(a1) || !Array.isArray(a2) || a1.length != a2.length)
            return false;
        for (var i = 0, n = a1.length; i < n; i++) {
            if (a1[i] !== a2[i])
                return false;
        }
        return true;
    }
})(mbe_ui || (mbe_ui = {}));
var mbe_ui;
(function (mbe_ui) {
    class Component {
        constructor() {
            this.hasPendingUpdate = false;
        }
        getView() { return this.settings; }
        update() {
            if (this.element) {
                this.renderer.render(this.settings, this.element);
            }
        }
        updateLater() {
            if (this.hasPendingUpdate)
                return;
            this.hasPendingUpdate = true;
            requestAnimationFrame(() => this.hasPendingUpdate && this.update());
        }
        shouldPreventUpdate() { return false; }
        onAttached() { }
        onRendered() { }
        onBeforeRemove() { }
    }
    mbe_ui.Component = Component;
})(mbe_ui || (mbe_ui = {}));
var mbe_ui;
(function (mbe_ui) {
    class UIDomer extends mbe_ui.Domer {
        onBeforeRender(velem, elem) {
            var Class = velem && velem.Class;
            if (Class) {
                var comp;
                if (elem) {
                    if (elem.Class === Class)
                        comp = elem.Component;
                    else if (elem.Component) {
                        this._finalize(elem.Component);
                    }
                }
                if (!comp) {
                    comp = new Class();
                    comp.renderer = this;
                }
                comp.settings = velem;
                comp.hasPendingUpdate = false;
                try {
                    if (comp.shouldPreventUpdate())
                        return false;
                    velem = comp.getView();
                }
                catch (e) {
                    this._onError(e);
                    return null;
                }
                velem.Class = Class;
                velem.Component = comp;
            }
            else if (elem && elem.Component) {
                this._finalize(elem.Component);
            }
            return velem;
        }
        onAttached(virtualElem, realElem) {
            if (realElem.Component) {
                realElem.Component.element = realElem;
                realElem.Component.onAttached();
            }
        }
        onRendered(virtualElem, realElem) {
            if (realElem.Component) {
                if (realElem.Component.element === realElem) {
                    realElem.Component.onRendered();
                }
                else if (!realElem.Component.element) {
                    realElem.Component.element = realElem;
                    realElem.Component.onAttached();
                }
                else {
                    throw new Error('UIDomer.onRendered(): unexpected');
                }
            }
        }
        onBeforeRemove(realElem) {
            for (var kid = realElem.firstElementChild; kid; kid = kid.nextElementSibling)
                this.onBeforeRemove(kid);
            if (realElem.Component) {
                this._finalize(realElem.Component);
            }
        }
        onError(e) {
            console.error(e);
        }
        _onError(e) {
            try {
                this.onError && this.onError(e);
            }
            catch (e) {
                console.error(e);
            }
        }
        _finalize(comp) {
            try {
                comp.onBeforeRemove();
            }
            catch (e) {
                this._onError(e);
            }
            finally {
                var el = comp.element;
                if (el) {
                    comp.hasPendingUpdate = false;
                    comp.element = null;
                    el.Component = null;
                    el.Class = null;
                }
            }
        }
    }
    mbe_ui.UIDomer = UIDomer;
})(mbe_ui || (mbe_ui = {}));
var mbrfp;
(function (mbrfp) {
    class ClassroomReceiveFilesProgressesView extends mbe_ui.Component {
        constructor() {
            super();
            this.tasks = [];
            this.progresses = {};
            this.classInfo = { className: '', students: [] };
            this.students = Object.create(null);
            fetch('/classInfo').then(res => {
                if (!res.ok)
                    throw 'H-' + res.status;
                return res.json();
            }).then(data => {
                console.log('加载 /classInfo ok');
                this.classInfo = data;
                this.classInfo.students.forEach(stu => this.students[stu.uid] = stu);
                const events = new RobustEventSource('/messages/admin');
                events.onmessage = (ev) => {
                    console.log('sse: ', ev.data);
                    var msg = JSON.parse(ev.data);
                    switch (msg.type) {
                        case 'ReceiveFiles':
                            this.tasks = msg.tasks;
                            this.tasks.reverse();
                            this.updateLater();
                            break;
                        case 'ReceiveFilesProgresses':
                            let inputProgresses = msg.progresses;
                            for (let tid in inputProgresses) {
                                const itp = inputProgresses[tid];
                                const tp = this.progresses[tid] || (this.progresses[tid] = {});
                                for (let uid in itp) {
                                    if (this.students[uid])
                                        tp[uid] = itp[uid];
                                }
                            }
                            this.updateLater();
                            break;
                    }
                };
            })
                .catch(err => {
                alert('连接本地服务器失败!' + err);
                console.log('加载 /classInfo 失败：' + err);
            });
        }
        getView() {
            const rows = [];
            rows.push({
                Name: 'thead',
                Kids: [
                    {
                        Name: 'tr',
                        Kids: [
                            { Name: 'td', Kids: ['文件名称'], className: 'filename', '-padding-left': '4em' },
                            { Name: 'td', Kids: ['完成接收人数'] }
                        ]
                    }
                ]
            });
            this.tasks.forEach(task => {
                let finished = 0, total = 0, tp = this.progresses[task.id];
                for (let uid in tp) {
                    if (tp[uid] >= task.fileSize)
                        ++finished;
                    ++total;
                }
                rows.push({
                    Name: 'tr',
                    Kids: [
                        { Name: 'td', Kids: [task.fileName], className: 'filename' },
                        { Name: 'td', Kids: [finished + '/' + total] }
                    ]
                });
            });
            const view = {
                Kids: [
                    {
                        '-text-align': 'center', '-line-height': '2.25em', '-margin': '16px 0',
                        '-background-color': '#f5f5f5',
                        Kids: ['2. 发送列表']
                    },
                    {
                        Name: 'table',
                        Kids: rows
                    }
                ]
            };
            return view;
        }
    }
    function main() {
        const root = document.getElementById('mbrfp-root');
        const renderer = new mbe_ui.UIDomer();
        renderer.render({ Class: ClassroomReceiveFilesProgressesView }, root);
    }
    mbrfp.main = main;
})(mbrfp || (mbrfp = {}));
//# sourceMappingURL=ClassroomReceiveFilesProgressesApp.js.map