var pi = handler.get_default_pi(); // peer information
var chat_msgs = [];

var svg_fullscreen = <svg viewBox="0 0 357 357">
    <path d="M51,229.5H0V357h127.5v-51H51V229.5z M0,127.5h51V51h76.5V0H0V127.5z M306,306h-76.5v51H357V229.5h-51V306z M229.5,0v51    H306v76.5h51V0H229.5z"/>
</svg>;
var svg_action = <svg viewBox="-91 0 512 512"><path d="M315 211H191L298 22a15 15 0 00-13-22H105c-6 0-12 4-14 10L1 281a15 15 0 0014 20h127L61 491a15 15 0 0025 16l240-271a15 15 0 00-11-25z"/></svg>;
var svg_display = <svg viewBox="0 0 640 512">
    <path d="M592 0H48A48 48 0 0 0 0 48v320a48 48 0 0 0 48 48h240v32H112a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16H352v-32h240a48 48 0 0 0 48-48V48a48 48 0 0 0-48-48zm-16 352H64V64h512z"/>
</svg>;
var svg_secure = <svg viewBox="0 0 347.97 347.97">
<path fill="#3F7D46" d="m317.31 54.367c-59.376 0-104.86-16.964-143.33-54.367-38.461 37.403-83.947 54.367-143.32 54.367 0 97.405-20.155 236.94 143.32 293.6 163.48-56.666 143.33-196.2 143.33-293.6zm-155.2 171.41-47.749-47.756 21.379-21.378 26.37 26.376 50.121-50.122 21.378 21.378-71.499 71.502z"/>
</svg>;
var svg_insecure = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z" fill="none" stroke="red" stroke-width="14.827"/><g fill="red"><path d="M238.802 115.023l-111.573 114.68-8.6-8.367L230.2 106.656z"/><path d="M125.559 108.093l114.68 111.572-8.368 8.601-114.68-111.572z"/></g></svg>;
var svg_insecure_relay = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z" fill="none" stroke="red" stroke-width="14.827"/><g fill="red"><path d="M231.442 247.498l-7.754-10.205c-17.268 12.441-38.391 17.705-59.478 14.822-21.087-2.883-39.613-13.569-52.166-30.088-25.916-34.101-17.997-82.738 17.65-108.42 32.871-23.685 78.02-19.704 105.172 7.802l-32.052 7.987 3.082 12.369 48.722-12.142-11.712-46.998-12.822 3.196 4.496 18.039c-31.933-24.008-78.103-25.342-112.642-.458-31.361 22.596-44.3 60.436-35.754 94.723 2.77 11.115 7.801 21.862 15.192 31.588 30.19 39.727 88.538 47.705 130.066 17.785z"/></g></svg>;
var svg_secure_relay = <svg viewBox="0 0 347.97 347.97"><path d="M317.469 61.615c-59.442 0-104.976-16.082-143.489-51.539-38.504 35.457-84.04 51.539-143.479 51.539 0 92.337-20.177 224.612 143.479 278.324 163.661-53.717 143.489-185.992 143.489-278.324z" fill="#3f7d46" stroke="#3f7d46" stroke-width="14.827"/><g fill="red"><path d="M231.442 247.498l-7.754-10.205c-17.268 12.441-38.391 17.705-59.478 14.822-21.087-2.883-39.613-13.569-52.166-30.088-25.916-34.101-17.997-82.738 17.65-108.42 32.871-23.685 78.02-19.704 105.172 7.802l-32.052 7.987 3.082 12.369 48.722-12.142-11.712-46.998-12.822 3.196 4.496 18.039c-31.933-24.008-78.103-25.342-112.642-.458-31.361 22.596-44.3 60.436-35.754 94.723 2.77 11.115 7.801 21.862 15.192 31.588 30.19 39.727 88.538 47.705 130.066 17.785z" fill="#fff"/></g></svg>;
var svg_recording_off = <svg t="1663505560063" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5393" width="32" height="32"><path d="M1002.666667 260.266667c-12.8-8.533333-29.866667-4.266667-42.666667 4.266666L725.333333 430.933333V298.666667c0-72.533333-55.466667-128-128-128H128C55.466667 170.666667 0 226.133333 0 298.666667v426.666666c0 72.533333 55.466667 128 128 128h469.333333c72.533333 0 128-55.466667 128-128v-132.266666l230.4 166.4c17.066667 12.8 46.933333 8.533333 59.733334-8.533334 4.266667-8.533333 8.533333-17.066667 8.533333-25.6V298.666667c0-17.066667-8.533333-29.866667-21.333333-38.4zM640 725.333333c0 25.6-17.066667 42.666667-42.666667 42.666667H128c-25.6 0-42.666667-17.066667-42.666667-42.666667V298.666667c0-25.6 17.066667-42.666667 42.666667-42.666667h469.333333c25.6 0 42.666667 17.066667 42.666667 42.666667v426.666666z m298.666667-81.066666L755.2 512 938.666667 379.733333v264.533334z" p-id="5394" fill="#8a8a8a"></path></svg>;
var svg_recording_on = <svg t="1663505598640" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5644" width="32" height="32"><path d="M1002.666667 260.266667c-12.8-8.533333-29.866667-4.266667-42.666667 4.266666L725.333333 430.933333V298.666667c0-72.533333-55.466667-128-128-128H128C55.466667 170.666667 0 226.133333 0 298.666667v426.666666c0 72.533333 55.466667 128 128 128h469.333333c72.533333 0 128-55.466667 128-128v-132.266666l230.4 166.4c17.066667 12.8 46.933333 8.533333 59.733334-8.533334 4.266667-8.533333 8.533333-17.066667 8.533333-25.6V298.666667c0-17.066667-8.533333-29.866667-21.333333-38.4z" p-id="5645" fill="#2C8CFF"></path></svg>;

var cur_window_state = view.windowState;
function check_state_change() {
    if (view.windowState != cur_window_state) {
        stateChanged();
    }
    self.timer(30ms, check_state_change);
}

if (is_linux) {
    check_state_change();
} else {
    view << event statechange {
        stateChanged();
    }
}

function get_id() {
    return handler.get_option('alias') || handler.get_id()
}

function stateChanged() {
    stdout.println('state changed from ' + cur_window_state + ' -> ' + view.windowState);
    cur_window_state = view.windowState;
    adjustBorder();
    adaptDisplay();
    if (cur_window_state != View.WINDOW_MINIMIZED) {
        view.focus = handler; // to make focus away from restore/maximize button, so that enter key work
    }
    var fs = view.windowState == View.WINDOW_FULL_SCREEN;
    var el = $(#fullscreen);
    if (el) el.attributes.toggleClass("active", fs);
    el = $(#maximize);
    if (el) {
        el.state.disabled = fs;
    }
    if (fs) {
        $(header).style.set {
            display: "none",
        };
    }
}

var header;
var old_window_state = View.WINDOW_SHOWN;

var is_edit_os_password;
class EditOsPassword: Reactor.Component {
    function render() {
        return <span style="margin-left: 12px; padding: 0 6px; display: inline-block;" .link>{svg_edit}</span>;
    }

    function onMouse(evt) {
        if (evt.type == Event.MOUSE_DOWN) {
            is_edit_os_password = true;
            editOSPassword();
        }
    }
}

function editOSPassword(login=false) {
    var p0 = handler.get_option('os-password');
    msgbox("custom-os-password", 'OS Password', p0, "", function(res=null) {
        if (!res) return;
        var a0 = handler.get_option('auto-login') != '';
        var p = (res.password || '').trim();
        var a = res.autoLogin || false;
        if (p == p0 && a == a0) return;
        if (p != p0) handler.set_option('os-password', p);
        if (a != a0) handler.set_option('auto-login', a ? 'Y' : '');
        if (p && login) {
            handler.input_os_password(p, true);
        }
      });
}

var recording = false;

class Header: Reactor.Component {
    this var conn_note = "";
    
    function this() {
        header = this;
    }

    function render() {
        var icon_conn;
        var title_conn;
        if (this.secure_connection && this.direct_connection) {
            icon_conn = svg_secure;
            title_conn = translate("Direct and encrypted connection");
        } else if (this.secure_connection && !this.direct_connection) {
            icon_conn = svg_secure_relay;
            title_conn = translate("Relayed and encrypted connection");
        } else if (!this.secure_connection && this.direct_connection) {
            icon_conn = svg_insecure;
            title_conn = translate("Direct and unencrypted connection");
        } else {
            icon_conn = svg_insecure_relay;
            title_conn = translate("Relayed and unencrypted connection");
        }
        var title = get_id();
        if (pi.hostname) title += "(" + pi.username + "@" + pi.hostname + ")";
        if ((pi.displays || []).length == 0) {
            return <div .ellipsis style="size:*;text-align:center;margin:* 0;">{title}</div>;
        }
        var screens = pi.displays.map(function(d, i) {
            return <div #screen class={pi.current_display == i ? "current" : ""}>
                {i+1}
            </div>;
        });
        updateWindowToolbarPosition();
        var style = "flow:horizontal;";
        if (is_osx) style += "margin:*";
        self.timer(1ms, updatePrivacyMode);
        self.timer(1ms, toggleMenuState);
        return <div style={style}>
            {is_osx || is_xfce ? "" : <span #fullscreen>{svg_fullscreen}</span>}
            <div #screens>
                <span #secure title={title_conn}>{icon_conn}</span>
                <div .remote-id>{get_id()}</div>
                <div style="flow:horizontal;border-spacing: 0.5em;">{screens}</div>
                {this.renderGlobalScreens()}
            </div>
            <span #chat>{svg_chat}</span>
            <span #action>{svg_action}</span>
            <span #display>{svg_display}</span>
            <span #keyboard>{svg_keyboard}</span>
            {recording_enabled ? <span #recording>{recording ? svg_recording_on : svg_recording_off}</span> : ""}
            {this.renderKeyboardPop()}
            {this.renderDisplayPop()}
            {this.renderActionPop()}
        </div>;
    }

    function renderKeyboardPop(){
        const is_map_mode_supported = handler.is_keyboard_mode_supported("map");
        const is_translate_mode_supported = handler.is_keyboard_mode_supported("translate");
        return <popup>
            <menu.context #keyboard-options>
                <li #legacy><span>{svg_checkmark}</span>{translate('Legacy mode')}</li>
                { is_map_mode_supported && <li #map><span>{svg_checkmark}</span>{translate('Map mode')}</li> }
                { is_translate_mode_supported && <li #translate><span>{svg_checkmark}</span>{translate('Translate mode')}</li> }
            </menu>
        </popup>;
    }

    function renderDisplayPop() {
        var codecs = handler.alternative_codecs();
        var show_codec = codecs[0] || codecs[1] || codecs[2] || codecs[3];

        var cursor_embedded = false;
        if ((pi.displays || []).length > 0) {
            if (pi.displays.length > pi.current_display) {
                cursor_embedded = pi.displays[pi.current_display].cursor_embedded;
            }
        }

        var is_file_copy_paste_supported = false;
        if (handler.version_cmp(pi.version, '1.2.4') < 0) {
            is_file_copy_paste_supported = is_win && pi.platform == "Windows";
        } else {
            is_file_copy_paste_supported = handler.has_file_clipboard() && pi.platform_additions?.has_file_clipboard;
        }

        return <popup>
            <menu.context #display-options>
                <li #adjust-window style="display:none">{translate('Adjust Window')}</li> 
                <div #adjust-window .separator style="display:none"/>
                <li #original type="view-style"><span>{svg_checkmark}</span>{translate('Original')}</li> 
                <li #shrink type="view-style"><span>{svg_checkmark}</span>{translate('Shrink')}</li> 
                <li #stretch type="view-style"><span>{svg_checkmark}</span>{translate('Stretch')}</li> 
                <div .separator />
                <li #best type="image-quality"><span>{svg_checkmark}</span>{translate('Good image quality')}</li> 
                <li #balanced type="image-quality"><span>{svg_checkmark}</span>{translate('Balanced')}</li> 
                <li #low type="image-quality"><span>{svg_checkmark}</span>{translate('Optimize reaction time')}</li> 
                <li #custom type="image-quality"><span>{svg_checkmark}</span>{translate('Custom')}</li>
                {show_codec ? <div>
                <div .separator />
                <li #auto type="codec-preference"><span>{svg_checkmark}</span>Auto</li>
                {codecs[0] ? <li #vp8 type="codec-preference"><span>{svg_checkmark}</span>VP8</li> : ""}
                <li #vp9 type="codec-preference"><span>{svg_checkmark}</span>VP9</li>
                {codecs[1] ? <li #av1 type="codec-preference"><span>{svg_checkmark}</span>AV1</li> : ""}
                {codecs[2] ? <li #h264 type="codec-preference"><span>{svg_checkmark}</span>H264</li> : ""}
                {codecs[3] ? <li #h265 type="codec-preference"><span>{svg_checkmark}</span>H265</li> : ""}
                </div> : ""}
                <div .separator />
                {!cursor_embedded && <li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li>}
                {<li #follow-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Follow remote cursor')}</li>}
                {<li #follow-remote-window .toggle-option><span>{svg_checkmark}</span>{translate('Follow remote window focus')}</li>}
                <li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li> 
                {audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
                {is_file_copy_paste_supported && file_enabled ? <li #enable-file-copy-paste .toggle-option><span>{svg_checkmark}</span>{translate('Enable file copy and paste')}</li> : ""}
                {keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""} 
                {keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""} 
                {keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
                {keyboard_enabled && ((is_osx && pi.platform != "Mac OS") || (!is_osx && pi.platform == "Mac OS")) ? <li #allow_swap_key  .toggle-option><span>{svg_checkmark}</span>{translate('Swap control-command key')}</li> : ""}
                {handler.version_cmp(pi.version, '1.2.4') >= 0 ? <li #i444><span>{svg_checkmark}</span>{translate('True color (4:4:4)')}</li> : ""}
            </menu>
        </popup>;
    }

    function renderActionPop() {
        return <popup>
            <menu.context #action-options>
                {keyboard_enabled ? <li #os-password>{translate('OS Password')}<EditOsPassword /></li> : ""}
                <li #transfer-file>{translate('Transfer file')}</li> 
                <li #tunnel>{translate('TCP tunneling')}</li> 
                {handler.get_audit_server("conn") && <li #note>{translate('Note')}</li>}
                <div .separator />
                {keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ? <li #ctrl-alt-del>{translate('Insert')} Ctrl + Alt + Del</li> : ""}
                {restart_enabled && (pi.platform == "Linux" || pi.platform == "Windows" || pi.platform == "Mac OS") ? <li #restart_remote_device>{translate('Restart remote device')}</li> : ""}
                {keyboard_enabled ? <li #lock-screen>{translate('Insert Lock')}</li> : ""}
                {keyboard_enabled && pi.platform == "Windows" && pi.sas_enabled ? <li #block-input>{translate("Block user input")}</li> : ""}
                {handler.is_screenshot_supported() ? <li #take-screenshot>{translate('Take screenshot')}</li> : "" }
                <li #refresh>{translate('Refresh')}</li>
            </menu>
        </popup>;
    }

    function renderGlobalScreens() {
        if (pi.displays.length < 3) return "";
        var x0 = 9999999;
        var y0 = 9999999;
        var x = -9999999;
        var y = -9999999;
        pi.displays.map(function(d, i) {
            if (d.x < x0) x0 = d.x;
            if (d.y < y0) y0 = d.y;
            var dx = d.x + d.width;
            if (dx > x) x = dx;
            var dy = d.y + d.height;
            if (dy > y) y = dy;
        });
        var w = x - x0;
        var h = y - y0;
        var scale = 16. / h;
        var screens = pi.displays.map(function(d, i) {
            var min_wh = d.width > d.height ? d.height : d.width;
            var fs = min_wh * 0.9 * scale;
            var style = "width:" + (d.width * scale) + "px;" +
                        "height:" + (d.height * scale) + "px;" +
                        "left:" + ((d.x - x0) * scale) + "px;" +
                        "top:" + ((d.y - y0) * scale) + "px;" +
                        "font-size:" + fs + "px;";
            if (is_osx) {
              style += "line-height:" + fs + "px;";
            }
            return <div style={style} class={pi.current_display == i ? "current" : ""}>{i+1}</div>;
        });

        var style = "width:" + (w * scale) + "px; height:" + (h * scale) + "px;";
        return <div #global-screens style={style}>
            {screens}
        </div>;
    }

    event click $(#fullscreen) (_, el) {
        if (view.windowState == View.WINDOW_FULL_SCREEN) {
            if (old_window_state == View.WINDOW_MAXIMIZED) {
                view.windowState = View.WINDOW_SHOWN;
            }
            view.windowState = old_window_state;
        } else {
            old_window_state = view.windowState;
            if (view.windowState == View.WINDOW_MAXIMIZED) {
                view.windowState = View.WINDOW_SHOWN;
            }
            view.windowState = View.WINDOW_FULL_SCREEN;
            if (is_linux) { self.timer(150ms, function() { view.windowState = View.WINDOW_FULL_SCREEN; }); }
        }
    }
    
    event click $(#chat) {
        startChat();
    }
    
    event click $(#action) (_, me) {
        var menu = $(menu#action-options);
        me.popup(menu);
    }

    event click $(#display) (_, me) {
        var menu = $(menu#display-options);
        me.popup(menu);
    }

    event click $(#keyboard) (_, me) {
        var menu = $(menu#keyboard-options);
        me.popup(menu);
    }

    event click $(span#recording) (_, me) {
        header.update();
        handler.record_screen(!recording)
    }

    event click $(#screen) (_, me) {
        if (pi.current_display == me.index) return;
        handler.switch_display(me.index);
    }
    
    event keyup (evt) {
        if((pi.displays || []).length > 0 && evt.keyCode == 220)
        { 
            if (pi.displays.length > pi.current_display) 
                handler.switch_display(pi.current_display + 1);
            else
                handler.switch_display(1);
        }
    }

    event click $(#transfer-file) {
        handler.transfer_file();
    }
    
    event click $(#os-password) (evt) {
        if (is_edit_os_password) {
            is_edit_os_password = false;
            return;
        }
        var p = handler.get_option('os-password');
        if (!p) editOSPassword(true);
        else handler.input_os_password(p, true);
    }

    event click $(#tunnel) {
        handler.tunnel();
    }

    event click $(#note) {
        var self = this;
        msgbox("custom", "Note", <div .form>
         <textarea .outline-focus spellcheck="false" name="text" novalue="input note here" style="overflow: scroll-indicator; width:*; height: 140px; font-size: 1.2em; padding: 0.5em;">{self.conn_note}</textarea>
          </div>, "", function(res=null) {
            if (!res) return;
            if (res.text == null || res.text == undefined) return;
            self.conn_note = res.text ?? "";
            handler.send_note(res.text);
          }, 280);
    }

    event click $(#ctrl-alt-del) {
        handler.ctrl_alt_del();
    }

    event click $(#restart_remote_device) {
        msgbox(
            "restart-confirmation",
            translate("Restart remote device"),
            translate("Are you sure you want to restart") + " " + pi.username + "@" + pi.hostname + "(" + get_id() + ") ?",
            "",
            function(res=null) {
                if (res != null) handler.restart_remote_device();
            }
        );
    }

    event click $(#lock-screen) {
        handler.lock_screen();
    }

    event click $(#take-screenshot) {
        handler.take_screenshot(pi.current_display, "");
    }
    
    event click $(#refresh) {
        // 0 is just a dummy value. It will be ignored by the handler.
        handler.refresh_video(0);
    }

    event click $(#block-input) {
        if (!input_blocked) {
            handler.toggle_option("block-input");
            input_blocked = true;
            $(#block-input).text = translate("Unblock user input");
        } else {
            handler.toggle_option("unblock-input");
            input_blocked = false;
            $(#block-input).text = translate("Block user input");
        }
    }

    event click $(menu#display-options li) (_, me) {
        if (me.id == "custom") {
            handle_custom_image_quality();
        } else if (me.id == "privacy-mode") {
            togglePrivacyMode(me.id);
        } else if (me.id == "show-quality-monitor") {
            toggleQualityMonitor(me.id);
        } else if (me.id == "i444") {
            toggleI444(me.id);
        } else if (me.attributes.hasClass("toggle-option")) {
            handler.toggle_option(me.id);
            toggleMenuState();
        } else if (!me.attributes.hasClass("selected")) {
            var type =  me.attributes["type"];
            if (type == "image-quality") {
                handler.save_image_quality(me.id);
            } else if (type == "view-style") {
                handler.save_view_style(me.id);
                adaptDisplay();
            } else if (type == "codec-preference") {
                handler.set_option("codec-preference", me.id);
                handler.update_supported_decodings();
            }
            toggleMenuState();
        }
    }

    event click $(menu#keyboard-options>li) (_, me) {
        if (me.id == "legacy") {
            handler.save_keyboard_mode("legacy");
        } else if (me.id == "map") {
            handler.save_keyboard_mode("map");
        } else if (me.id == "translate") {
            handler.save_keyboard_mode("translate");
        }
        toggleMenuState()
    }
}

function handle_custom_image_quality() {
    var tmp = handler.get_custom_image_quality();
    var bitrate = (tmp[0] || 50);
    var extendedBitrate = bitrate > 100;
    var maxRate = extendedBitrate ? 2000 : 100;
    msgbox("custom-image-quality", "Custom Image Quality", "<div .form> \
          <div><input #bitrate-slider type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"" + maxRate + "\" min=\"10\" value=\"" + bitrate + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% Bitrate <button|checkbox #extended-slider .custom-event " + (extendedBitrate ? "checked" : "") + ">More</button></div> \
      </div>", "", function(res=null) {
        if (!res) return;
        if (res.id === "extended-slider") {
            var slider = res.parent.$(#bitrate-slider)
            slider.slider.max = res.checked ? 2000 : 100;
            if (slider.value > slider.slider.max) {
                slider.value = slider.slider.max;
            }
            var buddy = res.parent.$(#bitrate-buddy);
            buddy.value = slider.value;
            return;
        }
        if (!res.bitrate) return;
        handler.save_custom_image_quality(res.bitrate);
        toggleMenuState();
      });
}

function toggleMenuState() {
    var values = [];
    var q = handler.get_image_quality();
    if (!q) q = "balanced";
    values.push(q);
    var s = handler.get_view_style();
    if (!s) s = "original";
    values.push(s);
    var k = handler.get_keyboard_mode();
    values.push(k);
    var c = handler.get_option("codec-preference");
    if (!c) c = "auto";
    values.push(c);
    for (var el in $$(menu#display-options li)) {
        el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
    }
    for (var el in $$(menu#keyboard-options>li)) {
        el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
    }
    for (var id in ["show-remote-cursor", "follow-remote-cursor", "follow-remote-window", "show-quality-monitor", "disable-audio", "enable-file-copy-paste", "disable-clipboard", "lock-after-session-end", "allow_swap_key", "i444"]) {
        var el = self.select('#' + id);
        if (el) {
            var value = handler.get_toggle_option(id);
            el.attributes.toggleClass("selected", value);
        }
    }
}

if (is_osx) {
    $(header).content(<Header />);
    $(header).attributes["role"] = "window-caption";
} else {
    if (is_file_transfer || is_port_forward) {
        $(caption).content(<Header />);
    } else {
        $(div.window-toolbar).content(<Header />);
    }
    setWindowButontsAndIcon();
}

if (!(is_file_transfer || is_port_forward)) {
    $(header).style.set {
        height: "32px",
    };
    if (!is_osx) {
        $(div.window-icon).style.set {
            size: "32px",
        };
    }
}

handler.updatePi = function(v) {
    pi = v;
    recording = handler.is_recording();
    header.update();
    if (is_port_forward) {
        view.windowState = View.WINDOW_MINIMIZED;
    }
}

handler.updateDisplays = function(v) {
    pi.displays = v;
    header.update();
    if (is_port_forward) {
        view.windowState = View.WINDOW_MINIMIZED;
    }
}

handler.setMultipleWindowsSession = function(sessions) {
    // It will be covered by other message box if the timer is not used, 
    self.timer(1000ms, function() {
        msgbox("multiple-sessions-nocancel", translate("Multiple Windows sessions found"), <MultipleSessionComponent sessions={sessions} />, "", function(res) {
            if (res && res.sid) {
                handler.set_selected_windows_session_id("" + res.sid);
            }
        }, 230);
    });
}

handler.setCurrentDisplay = function(v) {
    pi.current_display = v;
    handler.switch_display(v);
    header.update();
    if (is_port_forward) {
        view.windowState = View.WINDOW_MINIMIZED;
    }
}

handler.screenshot = function(msg) {
    if (msg) {
        msgbox(
            "custom-nocancel-nook-hasclose-error",
            translate("Take screenshot"),
            msg,
            "",
            function() {}
        );
    } else {
        msgbox(
            "custom-take-screenshot-nocancel-nook",
            translate("Take screenshot"),
            translate("screenshot-action-tip"),
            "",
            function() {}
        );
    }
}

function updatePrivacyMode() {
    var el = $(li#privacy-mode);
    if (el) {
        var supported = handler.is_privacy_mode_supported();
        if (!supported) {
            // el.attributes.toggleClass("line-through", true);
            el.style["display"]="none";
        } else {
            var value = handler.get_toggle_option("privacy-mode");
            el.attributes.toggleClass("selected", value);
            var el = $(li#block-input);
            if (el) {
                el.state.disabled = value;
            }
        }
    }
}
handler.updatePrivacyMode = updatePrivacyMode;

function togglePrivacyMode(privacy_id) {
    var supported = handler.is_privacy_mode_supported();
    if (!supported) {
        msgbox("nocancel", translate("Privacy mode"), translate("Unsupported"), "", function() { });
    } else {
        handler.toggle_option(privacy_id);
    }
}

function toggleQualityMonitor(name) {
    var show = handler.get_toggle_option(name);
    if (show) {
        $(#quality-monitor).style.set{ display: "none" };
    } else {
        $(#quality-monitor).style.set{ display: "block" };
    }
    handler.toggle_option(name);
    toggleMenuState();
}

function toggleI444(name) {
    handler.toggle_option(name);
    handler.update_supported_decodings();
    toggleMenuState();
}

handler.updateBlockInputState = function(input_blocked) {
    if (!input_blocked) {
        handler.toggle_option("block-input");
        input_blocked = true;
        $(#block-input).text = translate("Unblock user input");
    } else {
        handler.toggle_option("unblock-input");
        input_blocked = false;
        $(#block-input).text = translate("Block user input");
    }
}

handler.switchDisplay = function(i) {
    pi.current_display = i;
    header.update();
}

function updateWindowToolbarPosition() {
    if (is_osx) return;
    self.timer(1ms, function() {
        var el = $(div.window-toolbar);
        var w1 = el.box(#width, #border);
        var w2 = $(header).box(#width, #border);
        var x = (w2 - w1) / 2 / scaleFactor;
        el.style.set {
            left: x + "px",
            display: "block",
        };
    });
}

view.on("size", function() {
    // ensure size is done, so add timer
    self.timer(1ms, function() {
        updateWindowToolbarPosition();
        adaptDisplay();
    });
});

handler.newMessage = function(text) {
    chat_msgs.push({text: text, name: pi.username || "", time: getNowStr()});
    startChat();
}

function sendMsg(text) {
    chat_msgs.push({text: text, name: "me", time: getNowStr()});
    handler.send_chat(text);
    if (chatbox) chatbox.refresh();
}

var chatbox;
function startChat() {
    if (chatbox) {
        chatbox.windowState = View.WINDOW_SHOWN;
        chatbox.refresh();
        return;
    }
    var icon = handler.get_icon();
    var (sx, sy, sw, sh) = view.screenBox(#workarea, #rectw);
    var w = scaleIt(300);
    var h = scaleIt(400);
    var x = (sx + sw - w) / 2;
    var y = sy + scaleIt(80);
    var params = {
        type: View.FRAME_WINDOW,
        x: x,
        y: y,
        width: w,
        height: h,
        client: true,
        parameters: { msgs: chat_msgs, callback: sendMsg, icon: icon },
        caption: get_id(),
    };
    var html = handler.get_chatbox();
    if (html) params.html = html;
    else params.url = self.url("chatbox.html");
    chatbox = view.window(params);
}

handler.setConnectionType = function(secured, direct) {
    header.update({
       secure_connection: secured,
       direct_connection: direct, 
    });
}

handler.updateRecordStatus = function(status) {
    recording = status;
    header.update();
}