class RobustEventSource {
    constructor(_url) {
        this._url = _url;
        this._explictClosed = false;
        this._listenerLists = Object.create(null);
        this.CONNECTING = 0;
        this.OPEN = 1;
        this.CLOSED = 2;
        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] = []);
    }
}
RobustEventSource.CONNECTING = 0;
RobustEventSource.OPEN = 1;
RobustEventSource.CLOSED = 2;
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) {
                    if (rnode.data !== vnode)
                        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 mbcr;
(function (mbcr) {
    const scOptions = {
        'class-view': { type: 'ScreenCapture', on: true, fps: 0.25, maxWidth: 250, maxHeight: 150 },
        'hd-view': { type: 'ScreenCapture', on: true, ignoreScreenBroadcast: true, fps: 1, maxWidth: 1280, maxHeight: 1280 },
        'detail-view': { type: 'ScreenCapture', on: true, ignoreScreenBroadcast: true, fps: 1, maxWidth: 640, maxHeight: 640 }
    };
    class ClassroomConsoleApp {
        constructor(root) {
            this.root = root;
            this.classInfo = { className: '', students: [], teams: [] };
            this.students = Object.create(null);
            this.pongs = Object.create(null);
            this.pingStart = 0;
            this.pingEnd = 0;
            this.pingPaused = false;
            this.classroomState = {
                screenCapture: { type: 'ScreenCapture', on: false },
                screenBroadcast: { type: 'ScreenBroadcast', on: false },
                remoteDesktop: { type: 'RemoteDesktop', 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 },
                ping: { type: 'Ping', on: false },
                presenter: { type: 'Presenter', id: '' },
                teams: { type: 'Teams', teams: [] },
                countDown: { type: 'CountDown', on: false },
                selectStudent: { type: 'SelectStudent', on: false },
                lessonRecord: { type: 'LessonRecord', on: false, data: null },
                recordScreen: { type: 'RecordScreen', stop: true, cmd: 'delete' },
                studentDeviceControl: { type: 'StudentDeviceControl', powerOff: false },
                signIns: { type: 'SignIns', on: false }
            };
            this.screenCaptures = {};
            this.selectedStudents = {};
            this._viewMode = 'class-view';
            this._showStudentName = true;
            this._renderer = new mbe_ui.UIDomer();
            this._screenBroadcastTransition = false;
            this.sn = 0;
            this.refresh = this.refresh.bind(this);
            this.onKeyDown = this.onKeyDown.bind(this);
            window.addEventListener('resize', this.refresh);
            root.ownerDocument.addEventListener('keydown', this.onKeyDown);
            setInterval(this.refresh, 10000);
            fetch('/classInfo').then(res => {
                if (!res.ok)
                    throw '加载 /classInfo 失败';
                return res.json();
            }).then(data => {
                console.log('加载 /classInfo ok');
                this.classInfo = data;
                this.classInfo.students.forEach(stu => this.students[stu.uid] = stu);
                this.events = new RobustEventSource('/messages/admin');
                this.events.onmessage = (ev) => {
                    console.log('[' + Date.now() + '] sse: ', ev.data);
                    var msg = JSON.parse(ev.data);
                    switch (msg.type) {
                        case 'UpdateScreenCapture':
                            var id = msg.uid;
                            const screen = this.screenCaptures[id] || (this.screenCaptures[id] = {});
                            const stu = this.students[id];
                            stu.status = msg.status;
                            screen.date = new Date();
                            if (!stu.status) {
                                screen.url = '/screenCapture/' + id + '?sn=' + this.sn;
                                this.sn = (this.sn + 1) % 10000000;
                                console.log('[' + Date.now() + '] UpdateScreenCapture:url=' + screen.url);
                            }
                            break;
                        case 'ScreenCapture':
                            const sc = this.classroomState.screenCapture = msg;
                            if (!this.classroomState.screenCapture.on)
                                this.screenCaptures = {};
                            let m = 'class-view';
                            if (sc.uids) {
                                this.selectedStudents = {};
                                sc.uids.forEach(uid => this.selectStudent(uid, true));
                                if (sc.on) {
                                    if (sc.uids.length === 1)
                                        m = 'detail-view';
                                    else
                                        m = 'hd-view';
                                }
                            }
                            this.changeViewMode(m);
                            break;
                        case 'ScreenBroadcast':
                            this.classroomState.screenBroadcast = msg;
                            this._screenBroadcastTransition = false;
                            break;
                        case 'LockScreen':
                            this.classroomState.lockScreen = msg;
                            break;
                        case 'Join':
                            if (this.students[msg.uid])
                                this.students[msg.uid].connected = true;
                            break;
                        case 'Leave':
                            if (this.students[msg.uid])
                                this.students[msg.uid].connected = false;
                            break;
                        case 'Pong':
                            if (!this.pingPaused) {
                                this.pongs[msg.uid] = msg;
                                this.pingEnd = Date.now();
                            }
                            break;
                        case 'Ping':
                            this.classroomState.ping = msg;
                            this.pongs = Object.create(null);
                            this.pingPaused = false;
                            this.pingStart = this.pingEnd = Date.now();
                            break;
                        case 'StudentDeviceControl':
                            this.classroomState.studentDeviceControl.powerOff = msg.powerOff;
                            break;
                    }
                    this.refresh();
                };
                this.events.onerror = err => {
                    this.error = 'SSE 连接本地服务器失败，互联课堂将无法使用';
                    this.refresh();
                };
                this.events.onopen = () => {
                    if (this.error)
                        location.reload();
                };
            }).catch(err => {
                this.error = '连接本地服务器失败，互联课堂将无法使用';
                console.error(err);
                this.refresh();
            });
            this.refresh();
        }
        selectStudent(uid, selected) {
            if (selected)
                this.selectedStudents[uid] = this.students[uid];
            else
                delete this.selectedStudents[uid];
            this.refresh();
        }
        get viewMode() { return this._viewMode; }
        set viewMode(m) {
            this.changeViewMode(m);
            const option = scOptions[m];
            if (m !== 'class-view') {
                const stus = Object.keys(this.selectedStudents);
                option.uids = stus.length ? stus : undefined;
            }
            postJSON('/classroomControl/', option);
        }
        get showStudentName() { return this._showStudentName; }
        set showStudentName(show) {
            this._showStudentName = show;
            this.refresh();
        }
        get screenBroadcastTransition() { return this._screenBroadcastTransition; }
        setScreenBroadcast(on, disableMrdp) {
            this._screenBroadcastTransition = true;
            const msg = Object.assign({}, this.classroomState.screenBroadcast);
            msg.on = on;
            if (typeof disableMrdp === 'boolean')
                msg.disableMrdp = disableMrdp;
            postJSON('/classroomControl/', msg);
            this.refresh();
        }
        changeViewMode(m) {
            this._viewMode = m;
            if (m === 'class-view') {
                this.root.ownerDocument.exitFullscreen();
            }
            else {
                this.root.requestFullscreen();
            }
            this.refresh();
        }
        refresh() {
            const Class = this._viewMode === 'class-view' ? mbcr.ClassView : mbcr.DetailView;
            if (this.root.Component && this.root.Component instanceof Class)
                this.root.Component.updateLater();
            else
                this._renderer.render({ Class: Class, _app: this }, this.root);
        }
        onKeyDown(ev) {
            if (ev.keyCode === 80 && ev.altKey && !ev.shiftKey && !ev.ctrlKey) {
                if (this.classroomState.ping.on) {
                    if (this.pingPaused) {
                        postJSON('/classroomControl', { type: 'Ping', on: false });
                    }
                    else {
                        this.pingPaused = true;
                        this.refresh();
                        this.savePongResult();
                    }
                }
                else
                    postJSON('/classroomControl', { type: 'Ping', on: true });
            }
            else if (ev.keyCode === 77 && ev.altKey && !ev.shiftKey && !ev.ctrlKey) {
                this.setScreenBroadcast(true, !this.classroomState.screenBroadcast.disableMrdp);
            }
        }
        savePongResult() {
            const lines = ['用户名\tIP\t总数\t响应数\t错误率\t平均响应时间\t最大响应时间\r\n'];
            for (let id in this.students) {
                const pong = this.pongs[id];
                if (!pong)
                    continue;
                lines.push(this.students[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();
        }
    }
    mbcr.ClassroomConsoleApp = ClassroomConsoleApp;
    function postJSON(url, data) {
        return fetch(url, { method: 'POST', body: JSON.stringify(data), headers: {
                'Content-Type': 'application/json'
            } });
    }
    function main() {
        mbcr.app = new ClassroomConsoleApp(document.getElementById('mbcr-root'));
    }
    mbcr.main = main;
    if (!Element.prototype.requestFullscreen) {
        Element.prototype.requestFullscreen = Element.prototype.webkitRequestFullscreen;
    }
    if (!Document.prototype.exitFullscreen) {
        Document.prototype.exitFullscreen = Document.prototype.webkitCancelFullScreen || Document.prototype.webkitExitFullscreen;
    }
})(mbcr || (mbcr = {}));
var mbcr;
(function (mbcr) {
    var Component = mbe_ui.Component;
    class ClassView extends Component {
        getView() {
            const st = this.settings;
            if (st._app.viewMode !== 'class-view')
                debugger;
            const castCount = Object.keys(st._app.selectedStudents).length;
            let connectCount = 0;
            const total = st._app.classInfo.students.length;
            const disconnected = [], invisible = [];
            for (let stu of st._app.classInfo.students) {
                if (stu.connected) {
                    connectCount++;
                    const screen = st._app.screenCaptures[stu.uid];
                    if (stu.status === 'invisible')
                        invisible.push(stu.uname);
                }
                else {
                    disconnected.push(stu.uname);
                }
            }
            let sumTime = 0, okCount = 0, errorCount = 0;
            for (let id in st._app.pongs) {
                const p = st._app.pongs[id];
                sumTime += p.sumTime;
                okCount += p.okCount;
                errorCount += p.errorCount;
            }
            return {
                __proto__: st,
                className: 'class-view',
                Kids: [
                    { className: 'class-title',
                        Kids: [
                            (st._app.error ?
                                { Name: 'p', Kids: ['错误：' + st._app.error], '-color': 'red' } : ''),
                            { Name: 'p', Kids: ['当前班级：' + st._app.classInfo.className,
                                    { Name: 'span', '-margin-left': '2em', Kids: ['已连接：' + connectCount + '/' + total] }] },
                            (connectCount !== total ?
                                {
                                    Name: 'p',
                                    Kids: ['未连接的学生： ' + disconnected.join(', ')]
                                } : ''),
                            (invisible.length ?
                                {
                                    Name: 'p',
                                    Kids: ['离开学生端的学生： ' + invisible.join(', ')]
                                } : ''),
                            (st._app.classroomState.ping.on ?
                                {
                                    Name: 'p',
                                    Kids: ['ping 平均响应时间：' + Math.round(sumTime / okCount) + 'ms，响应数：' + okCount +
                                            '，丢失：' + Math.round(1000 * errorCount / (errorCount + okCount)) + '‰，' +
                                            '已运行：' + Math.round((st._app.pingEnd - st._app.pingStart) / 1000) + 's' +
                                            (st._app.pingPaused ? '（已暂停）' : '')
                                    ]
                                } : '')
                        ]
                    },
                    { className: 'student-list',
                        Kids: st._app.classInfo.students.map(stu => {
                            return {
                                Class: StudentSummary,
                                _app: st._app,
                                _info: stu,
                                _pong: st._app.pongs[stu.uid],
                            };
                        })
                    },
                    { className: 'error-status' },
                    { className: 'tool-bar  mbe-widget-container',
                        Kids: [
                            buildClassroomStateView(st._app),
                            {
                                Name: 'button', Kids: ['投屏显示 / 对比分析'],
                                disabled: !st._app.classroomState.screenCapture.on || !castCount,
                                onclick: () => {
                                    console.log('[' + Date.now() + '] 投屏显示 / 对比分析 onclick');
                                    st._app.showStudentName = false;
                                    st._app.viewMode = castCount > 1 ? 'detail-view' : 'hd-view';
                                },
                            },
                            {
                                Name: 'button', Kids: ['一键关闭所有学生端设备(优课平板)'],
                                disabled: st._app.classroomState.studentDeviceControl.powerOff,
                                onclick: () => {
                                    console.log('[' + Date.now() + '] 一键关闭所有学生端设备(优课平板) onclick');
                                    window['ucbook'] && window['ucbook'].postWebPageMessage('closeAllStudentDevices');
                                },
                            },
                            { className: 'mbe-widget-divider' },
                        ]
                    },
                ]
            };
        }
    }
    mbcr.ClassView = ClassView;
    function buildClassroomStateView(app) {
        const stateStr = [], state = app.classroomState;
        ;
        if (state.lockScreen.on)
            stateStr.push('锁屏');
        if (state.screenCapture.on)
            stateStr.push('屏幕监视');
        if (state.screenBroadcast.on)
            stateStr.push('屏幕广播' + (state.screenBroadcast.disableMrdp ? '' : '（多播）'));
        return { className: 'classroom-state mbe-widget-divider', Kids: ['课堂状态：' + (stateStr.length ? stateStr.join('，') : '-')] };
    }
    const dict = {
        __proto__: null,
        'invisible': '离开学生端',
        'lockScreen': '锁屏中',
        'screenBroadcast': '屏幕广播中',
    };
    function __(text) {
        return dict[text] || text;
    }
    function translateScreenStatus(st) {
        let text = '', className = '';
        const info = st._info, pong = st._pong;
        if (info.connected) {
            const screen = st._app.screenCaptures[info.uid], status = info.status;
            if (status) {
                text = __(status);
                className = status === 'invisible' ? 'masked-dark' : 'masked-light';
            }
            else {
                if (screen) {
                    const dt = Math.round((Date.now() - screen.date.valueOf()) / 1000);
                    if (dt >= 7) {
                        text = '截屏延迟：' + dt + 's';
                        className = 'masked-dark';
                    }
                }
                else {
                    text = '无截屏可用';
                    className = 'masked-dark';
                }
            }
        }
        else {
            text = '未连接';
            className = 'masked-dark';
        }
        if (pong) {
            className = 'masked-dark';
            text += '\nping：' + 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 : '') +
                '\n' + pong.ipAddress;
        }
        return { text, className };
    }
    class StudentSummary extends Component {
        getView() {
            const st = this.settings;
            const screen = st._app.screenCaptures[st._info.uid];
            const screenStatus = translateScreenStatus(st);
            return {
                __proto__: st,
                className: 'student-summary',
                Name: 'div',
                Kids: [
                    {
                        className: 'student-screen',
                        Kids: [
                            screen ? { Name: 'img', src: screen.url } : '',
                            {
                                className: screenStatus.className,
                                Kids: [
                                    {
                                        '.student-screen-status': true,
                                        Kids: [screenStatus.text]
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        Name: 'label',
                        className: 'student-summary-tool-bar',
                        Kids: [
                            { Kids: [st._info.uname] },
                            { Name: 'input', type: 'checkbox', checked: !!st._app.selectedStudents[st._info.uid],
                                onchange: ev => {
                                    st._app.selectStudent(st._info.uid, ev.target.checked);
                                },
                            }
                        ]
                    }
                ]
            };
        }
    }
    const kToolBarHeight = 70, kMarginV = 0, kInterval = 10, kAspectMany = 16 / 10, kAspectSingle = 1.85, kAspectDouble = 4 / 3;
    class DetailView extends Component {
        getView() {
            const listHeight = innerHeight - kToolBarHeight - kMarginV * 2;
            const st = this.settings;
            const sels = st._app.selectedStudents, selIds = Object.keys(sels).sort((a, b) => {
                const ka = sels[a].uname, kb = sels[b].uname;
                return ka < kb ? -1 : (ka > kb ? 1 : 0);
            });
            const single = selIds.length === 1, many = selIds.length > 2;
            const aspect = single ? kAspectSingle : (many ? kAspectMany : kAspectDouble);
            const subMaxW = Math.max(150, single ? innerWidth : (innerWidth - kInterval) / 2), subMaxH = Math.max(150, !many ? listHeight : (listHeight - kInterval) / 2), tall = subMaxH > subMaxW / aspect;
            const subW = Math.max(tall ? subMaxW : subMaxH * aspect, innerWidth / 3), subH = subW / aspect;
            return {
                __proto__: st,
                className: 'detail-view',
                Kids: [
                    { '.student-list': true, '.single': single, '.many': many,
                        '-margin-top': kMarginV + 'px', '-margin-bottom': kMarginV + 'px',
                        Kids: selIds.map((stu, i) => {
                            return {
                                Class: StudentDetail,
                                _app: st._app,
                                _info: sels[stu],
                                _pong: st._app.pongs[stu],
                                '-width': subW + 'px', '-height': subH + 'px',
                                '-margin-bottom': ((single || Math.ceil(selIds.length / 2) * 2 - i <= 2) ? 0 : kInterval) + 'px',
                                '-margin-right': ((i % 2 === 1 || single) ? 0 : kInterval) + 'px',
                                id: '' + i,
                            };
                        })
                    },
                    { className: 'tool-bar mbe-widget-container',
                        '-height': kToolBarHeight + 'px',
                        Kids: [
                            buildClassroomStateView(st._app),
                            { Name: 'button', Kids: [st._app.classroomState.screenBroadcast.on ? '结束广播' : '屏幕广播'],
                                disabled: st._app.screenBroadcastTransition || st._app.classroomState.lockScreen.on,
                                onclick: () => st._app.setScreenBroadcast(!st._app.classroomState.screenBroadcast.on),
                            },
                            { Name: 'button', Kids: [st._app.showStudentName ? '隐藏姓名' : '显示姓名'],
                                onclick: () => st._app.showStudentName = !st._app.showStudentName,
                            },
                            { Name: 'button', Kids: ['关闭'],
                                onclick: () => st._app.viewMode = 'class-view',
                            },
                            { className: 'mbe-widget-divider' },
                        ]
                    },
                ]
            };
        }
    }
    mbcr.DetailView = DetailView;
    class StudentDetail extends Component {
        getView() {
            const st = this.settings;
            const screen = st._app.screenCaptures[st._info.uid];
            const screenStatus = translateScreenStatus(st);
            console.log('[' + Date.now() + '] StudentDetail:getView(), url=' + (screen && screen.url));
            return {
                __proto__: st,
                Name: 'div',
                className: 'student-detail',
                Kids: [
                    {
                        className: 'student-screen',
                        Kids: [
                            screen ? { Name: 'img', src: screen.url, onload: ev => {
                                    console.log('[' + Date.now() + '] StudentDetail:img.onload(), url=' + screen.url + ',w=' +
                                        ev.target.naturalWidth);
                                } } : '',
                            {
                                className: screenStatus.className,
                                Kids: [
                                    {
                                        '.student-screen-status': true,
                                        Kids: [screenStatus.text]
                                    },
                                    {
                                        '.student-info': true,
                                        hidden: !st._app.showStudentName,
                                        Kids: [st._info.uname]
                                    }
                                ]
                            }
                        ]
                    },
                ]
            };
        }
    }
})(mbcr || (mbcr = {}));
//# sourceMappingURL=ClassroomConsoleApp.js.map