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 startTime = 0, lastUpdateTime = 0;
var refreshTimer = null;
var classInfo = null;
var screenCaptures = {};
var sn = 0;
var uidomer = new mbe_ui.UIDomer();
var loginControlWindow;
var ssids = ['', ''];
let clients = Object.create(null);
const spacer = { Name: 'span', '-margin': '0 2em' };
const classroomState = {
    screenCapture: { type: 'ScreenCapture', on: false },
    screenBroadcast: { type: 'ScreenBroadcast', on: false },
    lockScreen: { type: 'LockScreen', on: false },
    receiveFiles: { type: 'ReceiveFiles', tasks: [], urlTasks: [] },
    race: { type: 'Race', on: false },
    quiz: { type: 'Quiz', on: false },
    createTeams: { type: 'CreateTeams', on: false },
    teams: { type: 'Teams', teams: [] },
    countDown: { type: 'CountDown', on: false },
    selectStudent: { type: 'SelectStudent', on: false },
    ping: { type: 'Ping', on: false },
    presenter: { type: 'Presenter', id: '' },
    lessonRecord: { type: 'LessonRecord', on: false, data: null }
};
let quizType = 'single_selection';
let quizWindow;
let quizBody;
let answerSheets = Object.create(null);
let answerSheetMetadata = Object.create(null);
let pongs = Object.create(null);
let teamCount = 0;
let usersTeam = new Map();
let classControlData;
function refresh() {
    if (refreshTimer) {
        console.log('refresh already scheduled.');
        return;
    }
    else {
        refreshTimer = setTimeout(function () {
            console.log('refresh start...');
            refreshTimer = null;
            fetch('/classInfo').then(function (res) {
                if (!res.ok)
                    throw '加载 /classInfo 失败';
                return res.json();
            }).then(function (data) {
                console.log('refresh end.');
                classInfo = data;
                clients = Object.create(null);
                classInfo.students.forEach(u => clients[u.uid] = u);
                classInfo.devices.forEach(u => clients[u.uid] = u);
                updateUI();
            }).catch(function (err) {
                console.error(err);
            });
        }, 500);
    }
}
function createScreenUI(clientId) {
    var sc = screenCaptures[clientId], stu = classInfo.students[clientId];
    return {
        Name: 'td',
        Kids: [
            { className: 'screen-capture',
                Name: 'div',
                '-height': sc ? '100px' : '', '-width': '200px',
                Kids: [
                    { Name: 'img', src: sc && sc.url || '', hidden: !sc,
                        onmouseover: function (ev) { screenCaptureHD(clientId); },
                        onmouseout: stopScreenCaptureHD,
                    },
                    { Name: 'span', Kids: [clientId + (stu ? '/' + stu.uname : '') +
                                (sc ? (', dt:' + Math.round((Date.now() - +sc.date) / 1000)) : '')] },
                ]
            }
        ]
    };
}
function savePongResult() {
    const lines = ['用户名\tIP\t总数\t响应数\t错误率\t平均响应时间\t最大响应时间\r\n'];
    for (let id in clients) {
        const pong = pongs[id];
        if (!pong)
            continue;
        lines.push(clients[id].uname + '\t' + pong.ipAddress + '\t' + pong.pingCount + '\t' + pong.okCount + '\t' +
            (pong.errorCount / (pong.errorCount + pong.okCount)) + '\t' + (pong.sumTime / pong.okCount) + '\t' + pong.maxTime + '\r\n');
    }
    const blob = new Blob(lines, { type: 'text/plain' });
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = 'Ping测试结果-' + new Date().toLocaleString() + '.txt';
    document.body.appendChild(a);
    a.onclick = () => {
        setTimeout(() => {
            a.remove();
            URL.revokeObjectURL(a.href);
        }, 10);
    };
    a.click();
}
function updateUI() {
    var ve = {};
    if (classInfo) {
        const stat = classInfo.students.concat(classInfo.devices);
        stat.sort(function (i1, i2) {
            return i1.uid < i2.uid ? -1 : (i1.uid === i2.uid ? 0 : 1);
        });
        var head = { Name: 'thead', Kids: [
                { Name: 'tr', Kids: [
                        { Name: 'td', Kids: ['用户'] },
                        { Name: 'td', Kids: ['小组'] },
                        { Name: 'td', Kids: ['IP 地址'] },
                        { Name: 'td', Kids: ['Ping 结果'] },
                    ]
                }
            ]
        };
        var rows = stat.map(function (item) {
            const pong = pongs[item.uid];
            const pongText = pong ? '总数：' + pong.pingCount + '，接收：' + pong.okCount + '，丢失：' + pong.errorCount + '/' + Math.round(1000 * pong.errorCount / (pong.errorCount + pong.okCount)) +
                '‰，平均/最大：' + Math.round(pong.sumTime / pong.okCount) + '/' + pong.maxTime + 'ms' + (pong.error ? '，错误：' + pong.error : '')
                : '?';
            return {
                Name: 'tr',
                Kids: [
                    createScreenUI(item.uid),
                    { Name: 'td', Kids: ['' + (usersTeam.get(item.uid) || '未选组')] },
                    { Name: 'td', Kids: [(item.ipAddress || '?') + (item.connected ? '' : '（未连接）')] },
                    { Name: 'td', Kids: [pongText] },
                ]
            };
        });
        ve.Kids = [
            {
                Name: 'p',
                Kids: [
                    { Name: 'label', Kids: ['WIFI SSID（如果只有一个AP就只填写一个）: '] },
                    { Name: 'input', value: ssids[0], onchange: ev => ssids[0] = ev.target['value'] },
                    { Name: 'input', value: ssids[1], onchange: ev => ssids[1] = ev.target['value'] },
                    { Name: 'button', Kids: ['分配&连接WIFI'], onclick: assignSsid },
                    { Name: 'button', Kids: [classroomState.ping.on ? '停止 ping' : '开始 ping'], onclick: handlePing },
                ]
            },
            {
                Name: 'p',
                Kids: [
                    { Name: 'button', Kids: [classroomState.race.on ? '停止抢答' : '开始抢答'], onclick: setRace.bind(null, !classroomState.race.on) },
                    spacer,
                    { Name: 'label', Kids: ['课堂小测'], },
                    { Name: 'select', value: quizType, Kids: ['single_selection', 'multiple_selection', 'boolean_selection', 'subjective']
                            .map(value => { return { Name: 'option', Kids: [value], value }; }),
                        onchange: ev => quizType = ev.target['value'] },
                    { Name: 'input', type: 'file', multiple: true, accept: 'image/*',
                        onchange: ev => quizBody = ev.target['files'] },
                    { Name: 'button', Kids: [classroomState.quiz.on ? '停止小测' : '开始小测'], onclick: classroomState.quiz.on ? stopQuiz : startQuiz },
                    { Name: 'button', disabled: !classroomState.quiz.on || classroomState.quiz.expired, Kids: ['停止答题'], onclick: expireQuiz },
                    spacer,
                    { Name: 'input', type: 'number', step: 1, disabled: classroomState.createTeams.on,
                        oninput: ev => { teamCount = +ev.target['value']; updateUI(); } },
                    { Name: 'button', disabled: teamCount <= 1, Kids: [classroomState.createTeams.on ? '停止分组' : '开始分组'],
                        onclick: doCreateTeams },
                ]
            },
            {
                Name: 'p',
                Kids: [
                    { Name: 'input', size: 60, onchange: ev => classControlData = ev.target['value'] },
                    { Name: 'button', Kids: ['POST /classroomControl'],
                        onclick: () => fetch('/classroomControl', { method: 'POST', body: classControlData }).catch(err => alert(err)) },
                ]
            },
            { Name: 'p', Kids: ['经过的时间：' + Math.round(lastUpdateTime - startTime) + 'ms，客户端数量：' + stat.length] },
            {
                Name: 'table',
                Kids: [head].concat(rows)
            }
        ];
    }
    else {
        ve.Kids = ['请等待...'];
    }
    uidomer.render(ve, document.querySelector('#stat'));
}
function handlePing() {
    postJSON('/classroomControl', { type: 'Ping', on: !classroomState.ping.on });
    if (classroomState.ping.on)
        savePongResult();
}
function doCreateTeams() {
    postJSON('/classroomControl', { type: 'CreateTeams', on: !classroomState.createTeams.on, teamCount })
        .catch(err => alert('失败：' + err));
}
function setRace(on) {
    if (on) {
        postJSON('/classroomControl', { type: 'Race', on: true, id: '' + Math.random(), startTime: Date.now() + 5000 });
    }
    else {
        postJSON('/classroomControl', { type: 'Race', on: false });
    }
}
function startQuiz() {
    const p = quizBody ?
        Promise.all(Array.prototype.slice.call(quizBody, 0).map((file, i) => {
            const url = '/userContent/admin/' + i;
            return fetch(url, { method: 'PUT', body: file }).then(res => {
                if (!res.ok)
                    throw '上传失败：' + file.name;
                return url;
            });
        }))
        :
            Promise.resolve(null);
    p.then(urls => {
        const ques = { type: quizType, optionsCount: 4 };
        if (urls)
            ques.body = { images: urls };
        const quiz = { type: 'Quiz', on: true, id: '' + Math.random(), lifeSpan: 60000,
            questions: [ques] };
        return postJSON('/classroomControl', quiz);
    })
        .then(openQuizWindow)
        .catch(err => alert(err));
}
function stopQuiz() {
    postJSON('/classroomControl', { type: 'Quiz', on: false })
        .then(closeQuizWindow)
        .catch(err => alert(err));
}
function expireQuiz() {
    postJSON('/classroomControl', { type: 'Quiz', on: true, expired: true })
        .catch(err => alert(err));
}
function closeQuizWindow() {
    answerSheets = {};
    if (quizWindow) {
        if (!quizWindow.closed)
            quizWindow.close();
        quizWindow = null;
    }
}
function openQuizWindow() {
    closeQuizWindow();
    const wlc = window.open('login-control.htm');
    wlc.onload = () => renderQuizWindow(wlc.document);
    if (wlc.document && wlc.document.readyState === 'complete') {
        renderQuizWindow(wlc.document);
    }
    else {
        wlc.onload = () => renderQuizWindow(wlc.document);
    }
    quizWindow = wlc;
}
function renderQuizWindow(doc) {
    doc.title = '课堂小测';
    const uidomer = new mbe_ui.UIDomer({ doc: doc });
    uidomer.render({
        Name: '?',
        Kids: classInfo.students.map(user => {
            const ans = answerSheets[user.uid] && answerSheets[user.uid][0];
            const time = answerSheetMetadata[user.uid] && answerSheetMetadata[user.uid].timeUsed;
            let kids;
            if (ans != null) {
                if (ans['images']) {
                    kids = ans.images.map(url => {
                        return { Name: 'a', Kids: ['图片 '], target: '_blank', href: url };
                    });
                }
                else {
                    kids = [JSON.stringify(ans)];
                }
                kids.push(' 用时(s)：' + time / 1000);
            }
            else {
                kids = ['未提交'];
            }
            return {
                Name: 'p',
                Kids: [user.uname + '：', ...kids]
            };
        })
    }, doc.body);
}
function updateQuizState(quizState) {
    for (let uid in quizState.answerSheets) {
        answerSheets[uid] = quizState.answerSheets[uid];
        answerSheetMetadata[uid] = quizState.answerSheetMetadata[uid];
    }
    if (quizWindow && !quizWindow.closed && quizWindow.document.readyState === 'complete') {
        renderQuizWindow(quizWindow.document);
    }
}
function assignSsid() {
    const validSsids = ssids.filter(ssid => !!ssid);
    if (validSsids.length === 0) {
        alert('请填写SSID');
        return;
    }
    classInfo.devices.filter(info => info.connected).forEach((info, i, connected) => {
        const ssid = ssids[i * ssids.length / connected.length | 0];
        postJSON('/unicast/' + info.uid, { type: 'AssignSsid', value: ssid });
    });
}
function loginControl() {
    const wlc = window.open('login-control.htm');
    wlc.onload = () => renderLoginControl(wlc.document);
    if (wlc.document && wlc.document.readyState === 'complete') {
        renderLoginControl(wlc.document);
    }
    else {
        wlc.onload = () => renderLoginControl(wlc.document);
    }
    loginControlWindow = wlc;
}
function renderLoginControl(doc) {
    const uidomer = new mbe_ui.UIDomer({ doc: doc });
    const logins = JSON.parse(localStorage.getItem('logins') || '{}');
    const loginsByAccount = {};
    for (let did in logins)
        loginsByAccount[logins[did].account] = logins[did];
    const defaultAccounts = new Array(60);
    for (var i = 0; i < defaultAccounts.length; i++) {
        defaultAccounts[i] = 'hlkt' + (i < 9 ? '0' : '') + (i + 1);
    }
    const unusedAccounts = defaultAccounts.filter(a => !loginsByAccount[a]);
    const connected = {};
    let next = 0;
    classInfo.devices.forEach(info => {
        connected[info.uid] = info.connected;
        if (!logins[info.uid]) {
            const login = { type: 'Login', account: unusedAccounts[next], password: '000000' };
            next++;
            logins[info.uid] = login;
            loginsByAccount[login.account] = login;
        }
    });
    const devices = Object.keys(logins).sort((a, b) => logins[a].account.localeCompare(logins[b].account));
    function saveAndLogin() {
        Object.keys(logins).forEach(id => {
            if (!logins[id].account)
                delete logins[id];
        });
        localStorage.setItem('logins', JSON.stringify(logins));
        for (let id in logins) {
            if (connected[id]) {
                postJSON('/unicast/' + id, logins[id]);
            }
        }
    }
    const head = { Name: 'thead', Kids: [
            { Name: 'tr', Kids: [
                    { Name: 'td', Kids: ['设备ID'] },
                    { Name: 'td', Kids: ['账号'] },
                    { Name: 'td', Kids: ['口令'] },
                ]
            }
        ]
    };
    const rows = devices.map(id => {
        return { Name: 'tr', className: 'login-raw',
            Kids: [
                { Name: 'td', Kids: [id] },
                { Name: 'td', Kids: [{ Name: 'input', value: logins[id].account,
                            onchange: ev => logins[id] && (logins[id].account = ev.target.value) }] },
                { Name: 'td', Kids: [{ Name: 'input', value: logins[id].password,
                            onchange: ev => logins[id] && (logins[id].password = ev.target.value) }] },
            ]
        };
    });
    const view = {
        Name: '?',
        Kids: [
            { Name: 'button', Kids: ['刷新'], onclick: () => renderLoginControl(doc) },
            { Name: 'button', Kids: ['保存并登录'], onclick: saveAndLogin },
            { Name: 'table', '@style': 'border: 1px solid gray; border-collapse: collapse; font-size: 14px;',
                Kids: [head].concat(rows)
            }
        ]
    };
    uidomer.render(view, doc.body);
}
var events = new RobustEventSource('/messages/admin');
events.onmessage = function (ev) {
    console.log('sse: ', ev.data);
    if (ev.data === 'updateStatistics') {
        lastUpdateTime = performance.now();
        refresh();
    }
    else {
        var msg = JSON.parse(ev.data);
        switch (msg.type) {
            case 'UpdateScreenCapture':
                {
                    var id = msg.uid;
                    screenCaptures[id] = { url: '/screenCapture/' + id + '?sn=' + sn, status: msg.status, date: new Date() };
                    sn = (sn + 1) % 1000;
                    refresh();
                }
                break;
            case 'Join':
            case 'Leave':
                refresh();
                break;
            case 'Race':
                const race = classroomState.race = msg;
                if (race.on && race.winner) {
                    alert('抢答结果：' + race.winner.uname + '/'
                        + race.winner.uid);
                }
                updateUI();
                break;
            case 'Quiz':
                classroomState.quiz = msg;
                updateUI();
                break;
            case 'QuizState':
                updateQuizState(msg);
                break;
            case 'CreateTeams':
                classroomState.createTeams = msg;
                if (classroomState.createTeams.on)
                    usersTeam.clear();
                updateUI();
                break;
            case 'SelectTeam':
                let st = msg;
                usersTeam.set(st.uid, st.tid);
                updateUI();
                break;
            case 'Ping':
                classroomState.ping = msg;
                updateUI();
                break;
            case 'Pong':
                const pong = msg;
                pongs[pong.uid] = pong;
                updateUI();
                break;
            case 'LessonRecord':
                classroomState.lessonRecord = msg;
                updateUI();
                break;
        }
    }
};
var signalling = false;
var signallingBtn = document.getElementById('signalling');
signallingBtn.onclick = function () {
    postJSON('/classroomControl', { type: 'Signalling', on: !signalling });
    signalling = !signalling;
    signallingBtn.textContent = signalling ? '停止唤醒' : '开始唤醒';
};
document.getElementById('reset').onclick = resetUsers;
document.getElementById('kill').onclick = () => postJSON('/broadcast/admin', { type: 'Kill' });
document.getElementById('loginControl').onclick = loginControl;
document.getElementById('testVideo').onclick = function () {
    postJSON('/classroomControl', { type: 'ScreenBroadcast', on: true,
        ffmpeg: 'ffmpeg.exe',
        bitrate: 500, width: 1440, height: 800,
    });
};
document.getElementById('stopVideo').onclick = function () {
    postJSON('/classroomControl', { type: 'ScreenBroadcast', on: false
    });
};
document.getElementById('lockScreen').onclick = function () {
    postJSON('/classroomControl', { type: 'LockScreen', on: true });
};
document.getElementById('unlockScreen').onclick = function () {
    postJSON('/classroomControl', { type: 'LockScreen', on: false });
};
document.getElementById('sendFile').onclick = function () {
    const paths = document.getElementById('path')['value'].split(';');
    if (paths.length == 1) {
        const isApk = /\.apk$/i.test(paths[0]);
        postJSON('/classroomControl', { type: isApk ? 'SendInstallFile' : 'SendFile', path: paths[0] });
    }
    else if (paths.length > 1) {
        postJSON('/classroomControl', { type: 'SendFiles', paths: paths });
    }
};
document.getElementById('dropPacket').onclick = function () {
    const ratio = parseFloat(document.getElementById('packetDropRatio')['value']);
    postJSON('/classroomControl', { type: 'DropPacket', ratio: ratio });
};
document.getElementById('clearCache').onclick = function () {
    postJSON('/classroomControl', { type: 'ClearCache' });
};
function resetUsers() {
    const students = [];
    for (let i = 0, uid = 1962046; i < 50; i++) {
        students.push({ uid: '' + (uid - i), uname: '' + (i + 1) });
    }
    postJSON('/classroomControl/', { type: 'Reset', classInfo: { className: '12', students: students
        } }).then(res => {
        refresh();
    });
}
var scOptions = { type: 'ScreenCapture', on: true, fps: 0.25, maxWidth: 250, maxHeight: 150 }, scHdOptions = { type: 'ScreenCapture', on: true, ignoreScreenBroadcast: true, fps: 1, maxWidth: 800, maxHeight: 800 };
document.getElementById('monitorScreen').onclick = function () {
    screenCaptures = {};
    postJSON('/classroomControl', scOptions);
    refresh();
};
document.getElementById('stopMonitorScreen').onclick = function () {
    screenCaptures = {};
    postJSON('/classroomControl', { type: 'ScreenCapture', on: false });
};
function screenCaptureHD(clientId) {
    scHdOptions.uids = [clientId];
    postJSON('/classroomControl/', scHdOptions);
}
function stopScreenCaptureHD() {
    postJSON('/classroomControl/', scOptions);
}
function postJSON(url, data) {
    return fetch(url, { method: 'POST', body: JSON.stringify(data), headers: {
            'Content-Type': 'application/json'
        } })
        .then(res => {
        if (res.status !== 200)
            throw 'HTTP-' + res.status;
        return res;
    });
}
function main() {
    if (location.origin === 'null') {
        const base = document.createElement('base');
        base.href = 'http://localhost:7017/';
        document.head.appendChild(base);
    }
    refresh();
}
main();
//# sourceMappingURL=teacher.js.map