include "sciter:reactor.tis";
var handler = $(#handler) || view;
try { view.windowIcon = self.url(handler.get_icon()); } catch(e) {}
var OS = view.mediaVar("platform");
var is_osx = OS == "OSX";
var is_win = OS == "Windows";
var is_linux = OS == "Linux";
var is_file_transfer;
var is_xfce = false;
try { is_xfce = handler.is_xfce(); } catch(e) {}
function isEnterKey(evt) {
return (evt.keyCode == Event.VK_ENTER ||
(is_osx && evt.keyCode == 0x4C) ||
(is_linux && evt.keyCode == 65421));
}
function getScaleFactor() {
if (!is_win) return 1;
return self.toPixels(10000dip) / 10000.;
}
var scaleFactor = getScaleFactor();
view << event resolutionchange {
scaleFactor = getScaleFactor();
}
function scaleIt(x) {
return (x * scaleFactor).toInteger();
}
stdout.println("scaleFactor", scaleFactor);
function translate(name) {
try {
return handler.t(name);
} catch(_) {
return name;
}
}
function hashCode(str) {
var hash = 160 << 16 + 114 << 8 + 91;
for (var i = 0; i < str.length; i += 1) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
return hash % 16777216;
}
function intToRGB(i, a = 1) {
return 'rgba(' + ((i >> 16) & 0xFF) + ', ' + ((i >> 8) & 0x7F)
+ ',' + (i & 0xFF) + ',' + a + ')';
}
function string2RGB(s, a = 1) {
return intToRGB(hashCode(s), a);
}
function getTime() {
var now = new Date();
return now.valueOf();
}
function platformSvg(platform, color) {
platform = (platform || "").toLowerCase();
if (platform == "linux") {
return ;
}
if (platform == "mac os") {
return ;
}
if (platform == "android") {
return ;
}
return ;
}
function centerize(w, h) {
var (sx, sy, sw, sh) = view.screenBox(#workarea, #rectw);
if (w > sw) w = sw;
if (h > sh) h = sh;
var x = (sx + sw - w) / 2;
var y = (sy + sh - h) / 2;
view.move(x, y, w, h);
}
function setWindowButontsAndIcon(only_min=false) {
if (only_min) {
$(div.window-buttons).content(
);
} else {
$(div.window-buttons).content(
);
}
$(div.window-icon>icon).style.set {
"background-image": "url('" + handler.get_icon() + "')",
};
}
function adjustBorder() {
if (is_osx) {
if (view.windowState == View.WINDOW_FULL_SCREEN) {
$(header).style.set {
display: "none",
};
} else {
$(header).style.set {
display: "block",
padding: "0",
};
}
return;
}
if (view.windowState == view.WINDOW_MAXIMIZED) {
self.style.set {
border: "window-frame-width solid transparent",
};
} else if (view.windowState == view.WINDOW_FULL_SCREEN) {
self.style.set {
border: "none",
};
} else {
self.style.set {
border: "black solid 1px",
};
}
var el = $(button#maximize);
if (el) el.attributes.toggleClass("restore", view.windowState == View.WINDOW_MAXIMIZED);
el = $(span#fullscreen);
if (el) el.attributes.toggleClass("active", view.windowState == View.WINDOW_FULL_SCREEN);
}
var svg_checkmark = ;
var svg_edit = ;
var svg_eye = ;
var svg_send = ;
var svg_chat = ;
var svg_keyboard = ;
function scrollToBottom(el) {
var y = el.box(#height, #content) - el.box(#height, #client);
el.scrollTo(0, y);
}
function getNowStr() {
var now = new Date();
return String.printf("%02d:%02d:%02d", now.hour, now.minute, now.second);
}
/******************** start of chatbox ****************************************/
class ChatBox: Reactor.Component {
this var msgs = [];
this var callback;
function this(params) {
if (params) {
this.msgs = params.msgs || [];
this.callback = params.callback;
}
}
function renderMsg(msg) {
var cls = msg.name == "me" ? "right-side msg" : "left-side msg";
return
{msg.name == "me" ?
{msg.time + " "} me
:
{msg.name} {" " + msg.time}
}
{msg.text}
;
}
function render() {
var me = this;
var msgs = this.msgs.map(function(msg) { return me.renderMsg(msg); });
self.timer(1ms, function() {
scrollToBottom(me.msgs);
});
return
{msgs}
{svg_send}
;
}
function send() {
var el = this.$(input);
var value = (el.value || "").trim();
el.value = "";
if (!value) return;
if (this.callback) this.callback(value);
}
event keydown $(input) (evt) {
if (!evt.shortcutKey) {
if (isEnterKey(evt)) {
this.send();
}
}
}
event click $(div.send span) {
this.send();
view.focus = $(input);
}
}
/******************** end of chatbox ****************************************/
/******************** start of msgbox ****************************************/
var remember_password = false;
function msgbox(type, title, content, link="", callback=null, height=180, width=500, hasRetry=false, contentStyle="") {
$(body).scrollTo(0, 0);
if (!type) {
closeMsgbox();
return;
}
var remember = false;
try { remember = handler.get_remember(); } catch(e) {}
var auto_login = false;
try { auto_login = handler.get_option("auto-login") != ''; } catch(e) {}
width += is_xfce ? 50 : 0;
height += is_xfce ? 50 : 0;
if (type.indexOf("input-password") >= 0) {
callback = function (res) {
if (!res) {
view.close();
return;
}
handler.login(res.password, res.remember);
if (!is_port_forward) {
// Specially handling file transfer for no permission hanging issue (including 60ms
// timer in setPermission.
// For wrong password input hanging issue, we can not use handler.msgbox.
// But how about wrong password for file transfer?
if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in...");
else msgbox("connecting", "Connecting...", "Logging in...");
}
};
} else if (type.indexOf("custom") < 0 && !is_port_forward && !callback) {
callback = function() { view.close(); }
}
$(#msgbox).content();
}
function connecting() {
handler.msgbox("connecting", "Connecting...", "Connection in progress. Please wait.");
}
handler.msgbox = function(type, title, text, link = "", hasRetry=false) {
// crash somehow (when input wrong password), even with small time, for example, 1ms
self.timer(60ms, function() { msgbox(type, title, text, link, null, 180, 500, hasRetry); });
}
var reconnectTimeout = 1000;
handler.msgbox_retry = function(type, title, text, link, hasRetry) {
handler.msgbox(type, title, text, link, hasRetry);
if (hasRetry) {
self.timer(0, retryConnect);
self.timer(reconnectTimeout, retryConnect);
reconnectTimeout *= 2;
} else {
reconnectTimeout = 1000;
}
}
function retryConnect(cancelTimer=false) {
if (cancelTimer) self.timer(0, retryConnect);
if (!is_port_forward) connecting();
handler.reconnect();
}
/******************** end of msgbox ****************************************/
function Progress()
{
var _val;
var pos = -0.25;
function step() {
if( _val !== undefined ) { this.refresh(); return false; }
pos += 0.02;
if( pos > 1.25)
pos = -0.25;
this.refresh();
return true;
}
function paintNoValue(gfx)
{
var (w,h) = this.box(#dimension,#inner);
var x = pos * w;
w = w * 0.25;
gfx.fillColor( this.style#color )
.pushLayer(#inner-box)
.rectangle(x,0,w,h)
.popLayer();
return true;
}
this[#value] = property(v) {
get return _val;
set {
_val = undefined;
pos = -0.25;
this.paintContent = paintNoValue;
this.animate(step);
this.refresh();
}
}
this.value = "";
}
var svg_eye_cross = ;
class PasswordComponent: Reactor.Component {
this var visible = false;
this var value = '';
this var name = 'password';
function this(params) {
if (params && params.value) {
this.value = params.value;
}
if (params && params.name) {
this.name = params.name;
}
}
function render() {
return
{this.visible ? svg_eye_cross : svg_eye}
;
}
event click $(svg) {
var el = this.$(input);
var value = el.value;
var start = el.xcall(#selectionStart) || 0;
var end = el.xcall(#selectionEnd);
this.update({ visible: !this.visible });
var me = this;
self.timer(30ms, function() {
var el = me.$(input);
view.focus = el;
el.value = value;
el.xcall(#setSelection, start, end);
});
}
}
// type: #post, #get, #delete, #put
function httpRequest(url, type, params, _onSuccess, _onError, headers="") {
if (type != #post) {
stderr.println("only post ok");
}
handler.post_request(url, JSON.stringify(params), headers);
function check_status() {
var status = handler.get_async_job_status();
if (status == " ") self.timer(0.1s, check_status);
else {
try {
var data = JSON.parse(status);
_onSuccess(data);
} catch (e) {
_onError(status, 0);
}
}
}
check_status();
}
function isReasonableSize(r) {
var x = r[0];
var y = r[1];
var n = scaleIt(3200);
return !(x < -n || x > n || y < -n || y > n);
}
function awake() {
view.windowState = View.WINDOW_SHOWN;
view.focus = self;
}