//=================================================================//
// VARIABLES
//=================================================================//
// Socket IO Object
var socket;
// UI references for jQuery
var connect_status;
var button_newgame;
var button_rndgame;
var button_save;
var button_saveas;
var button_savetofile;
var button_load;
var button_import;
var button_importwi;
var button_impaidg;
var button_settings;
var button_format;
var button_softprompt;
var button_userscripts;
var button_mode;
var button_mode_label;
var button_send;
var button_actmem;
var button_actback;
var button_actfwd;
var button_actretry;
var button_actwi;
var game_text;
var input_text;
var message_text;
var chat_name;
var settings_menu;
var format_menu;
var wi_menu;
var anote_menu;
var anote_input;
var anote_labelcur;
var anote_slider;
var debug_area;
var popup;
var popup_title;
var popup_content;
var popup_accept;
var popup_close;
var aidgpopup;
var aidgpromptnum;
var aidg_accept;
var aidg_close;
var saveaspopup;
var saveasinput;
var savepins;
var topic;
var saveas_accept;
var saveas_close;
var loadpopup;
var loadcontent;
var load_accept;
var load_close;
var sppopup;
var spcontent;
var sp_accept;
var sp_close;
var uspopup;
var uscontent;
var us_accept;
var us_close;
var nspopup;
var ns_accept;
var ns_close;
var rspopup;
var rs_accept;
var rs_close;
var seqselmenu;
var seqselcontents;
var storyname = null;
var memorymode = false;
var memorytext = "";
var gamestarted = false;
var wiscroll = 0;
var editmode = false;
var connected = false;
var newly_loaded = true;
var modified_chunks = new Set();
var empty_chunks = new Set();
var gametext_bound = false;
var saved_prompt = "...";
var wifolders_d = {};
var wifolders_l = [];
var override_focusout = false;
var sman_allow_delete = false;
var sman_allow_rename = false;
var allowsp = false;
var remote = false;
var gamestate = "";
var gamesaved = true;
var modelname = null;
// This is true iff [we're in macOS and the browser is Safari] or [we're in iOS]
var using_webkit_patch = true;
// Key states
var shift_down = false;
var do_clear_ent = false;
// Display vars
var allowtoggle = false;
var formatcount = 0;
var allowedit = true; // Whether clicking on chunks will edit them
// Adventure
var action_mode = 0; // 0: story, 1: action
var adventure = false;
// Chatmode
var chatmode = false;
var sliders_throttle = getThrottle(200);
//=================================================================//
// METHODS
//=================================================================//
/**
* Returns a function that will automatically wait for X ms before executing the callback
* The timer is reset each time the returned function is called
* Useful for methods where something is overridden too fast
* @param ms milliseconds to wait before executing the callback
* @return {(function(*): void)|*} function that takes the ms to wait and a callback to execute after the timer
*/
function getThrottle(ms) {
var timer = {};
return function (id, callback) {
if (timer[id]) {
clearTimeout(timer[id]);
}
timer[id] = setTimeout(function () {
callback();
delete timer[id];
}, ms);
}
}
function addSetting(ob) {
// Add setting block to Settings Menu
if(ob.uitype == "slider"){
settings_menu.append("
\
\
\
"+ob.label+" ?"+ob.tooltip+"\
\
\
\
\
\
\
\
\
"+ob.min+"\
\
\
"+ob.max+"\
\
\
");
// Set references to HTML objects
var refin = $("#"+ob.id);
var reflb = $("#"+ob.id+"cur");
window["setting_"+ob.id] = refin; // Is this still needed?
window["label_"+ob.id] = reflb; // Is this still needed?
// Add event function to input
var send = function () {
sliders_throttle(ob.id, function () {
socket.send({'cmd': $(refin).attr('id'), 'data': $(refin).val()});
});
reflb.val($(refin).val());
}
refin.on("input", send);
reflb.on("change", function (event) {
var value = (ob.unit === "float" ? parseFloat : parseInt)(event.target.value);
if(Number.isNaN(value) || value > ob.max || value < ob.min) {
event.target.value = refin.val();
return;
}
refin.val(value);
send();
});
} else if(ob.uitype == "toggle"){
settings_menu.append("
\
\
"+ob.label+" \
?"+ob.tooltip+"\
");
// Tell Bootstrap-Toggle to render the new checkbox
$("input[type=checkbox]").bootstrapToggle();
$("#"+ob.id).on("change", function () {
if(allowtoggle) {
socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')});
}
if(ob.id == "setadventure"){
setadventure($(this).prop('checked'));
}
});
}
}
function refreshTitle() {
var title = gamesaved ? "" : "\u2731 ";
if(storyname !== null) {
title += storyname + " \u2014 ";
}
title += "KoboldAI Client";
if(modelname !== null) {
title += " (" + modelname + ")";
}
document.title = title;
}
function setGameSaved(state) {
gamesaved = !!state;
refreshTitle();
}
function addFormat(ob) {
// Check if we need to make a new column for this button
if(formatcount == 0) {
format_menu.append("");
}
// Get reference to the last child column
var ref = $("#formatmenu > div").last();
// Add format block to Format Menu
ref.append("
\
\
"+ob.label+" \
?"+ob.tooltip+"\
");
// Tell Bootstrap-Toggle to render the new checkbox
$("input[type=checkbox]").bootstrapToggle();
// Add event to input
$("#"+ob.id).on("change", function () {
if(allowtoggle) {
socket.send({'cmd': $(this).attr('id'), 'data': $(this).prop('checked')});
}
});
// Increment display variable
formatcount++;
if(formatcount == 2) {
formatcount = 0;
}
}
function addImportLine(ob) {
popup_content.append("
");
}
adjustWiCommentHeight($("#wicomment"+ob.num)[0]);
// Send key value to text input
$("#wikey"+ob.num).val(ob.key);
$("#wikeyprimary"+ob.num).val(ob.key);
$("#wikeysecondary"+ob.num).val(ob.keysecondary);
// Assign delete event to button
$("#btn_wi"+ob.num).on("click", function () {
showWiDeleteConfirm(ob.num);
});
} else {
// Show WI line item with form fields hidden (uninitialized)
current_wifolder_element.append("
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
");
// Assign function to expand WI item to button
$("#btn_wi"+ob.num).on("click", function () {
var folder = $("#wilistitem"+ob.num).parent().attr("folder-uid");
if(folder === undefined) {
folder = null;
} else {
folder = parseInt(folder);
}
socket.send({'cmd': 'wiexpand', 'data': ob.num});
socket.send({'cmd': 'wiinit', 'folder': folder, 'data': ob.num});
});
}
// Assign actions to other elements
wientry_onfocus = function () {
$("#selective-key-"+ob.num).addClass("selective-key-icon-clickthrough");
$("#constant-key-"+ob.num).addClass("constant-key-icon-clickthrough");
}
wientry_onfocusout = function () {
$("#selective-key-"+ob.num).removeClass("selective-key-icon-clickthrough");
$("#constant-key-"+ob.num).removeClass("constant-key-icon-clickthrough");
// Tell server about updated WI fields
var selective = $("#wilistitem"+ob.num)[0].classList.contains("wilistitem-selective");
socket.send({'cmd': 'wiupdate', 'num': ob.num, 'data': {
key: selective ? $("#wikeyprimary"+ob.num).val() : $("#wikey"+ob.num).val(),
keysecondary: $("#wikeysecondary"+ob.num).val(),
content: $("#wientry"+ob.num).val(),
comment: $("#wicomment"+ob.num).val(),
}});
}
$("#wikey"+ob.num).on("focus", wientry_onfocus);
$("#wikeyprimary"+ob.num).on("focus", wientry_onfocus);
$("#wikeysecondary"+ob.num).on("focus", wientry_onfocus);
$("#wientry"+ob.num).on("focus", wientry_onfocus);
$("#wicomment"+ob.num).on("focus", wientry_onfocus);
$("#wikey"+ob.num).on("focusout", wientry_onfocusout);
$("#wikeyprimary"+ob.num).on("focusout", wientry_onfocusout);
$("#wikeysecondary"+ob.num).on("focusout", wientry_onfocusout);
$("#wientry"+ob.num).on("focusout", wientry_onfocusout);
$("#wicomment"+ob.num).on("focusout", wientry_onfocusout);
$("#btn_wican"+ob.num).on("click", function () {
hideWiDeleteConfirm(ob.num);
});
$("#btn_widel"+ob.num).on("click", function () {
socket.send({'cmd': 'widelete', 'data': ob.uid});
});
$("#selective-key-"+ob.num).on("click", function () {
var element = $("#selective-key-"+ob.num);
if(element.hasClass("selective-key-icon-enabled")) {
socket.send({'cmd': 'wiseloff', 'data': ob.num});
} else {
socket.send({'cmd': 'wiselon', 'data': ob.num});
}
});
$("#constant-key-"+ob.num).on("click", function () {
var element = $("#constant-key-"+ob.num);
if(element.hasClass("constant-key-icon-enabled")) {
socket.send({'cmd': 'wiconstantoff', 'data': ob.num});
} else {
socket.send({'cmd': 'wiconstanton', 'data': ob.num});
}
});
$("#wihandle"+ob.num).off().on("mousedown", function () {
wientry_onfocusout()
$(".wisortable-container").addClass("wisortable-excluded");
// Prevent WI entries with extremely long comments from filling the screen and preventing scrolling
$(this).parent().css("max-height", "200px").find(".wicomment").find(".form-control").css("max-height", "110px");
}).on("mouseup", function () {
$(".wisortable-excluded-dynamic").removeClass("wisortable-excluded-dynamic");
$(this).parent().css("max-height", "").find(".wicomment").find(".form-control").css("max-height", "");
});
}
function addWiFolder(uid, ob) {
if(uid !== null) {
var uninitialized = $("#wilistfoldercontainer"+null);
var html = "
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
";
if(uninitialized.length) {
$(html).insertBefore(uninitialized);
} else {
wi_menu.append(html);
}
var onfocusout = function () {
socket.send({'cmd': 'wifolderupdate', 'uid': uid, 'data': {
name: $("#wifoldername"+uid).val(),
collapsed: !$("#btn_wifolderexpand"+uid).hasClass("folder-expanded"),
}});
};
$("#wifoldergutter"+uid).on("click", function () {
$(this).siblings(".wilistfolder")[0].scrollIntoView();
});
$("#btn_wifolder"+uid).on("click", function () {
showWiFolderDeleteConfirm(uid);
});
$("#btn_wifolderdel"+uid).on("click", function () {
socket.send({'cmd': 'wifolderdelete', 'data': uid});
});
$("#btn_wifoldercan"+uid).on("click", function () {
hideWiFolderDeleteConfirm(uid);
})
$("#wifoldername"+uid).on("focusout", onfocusout);
$("#wifolderhandle"+uid).off().on("mousedown", function () {
onfocusout();
$(".wilistitem, .wisortable-dummy").addClass("wisortable-excluded-dynamic");
// Prevent WI folders with extremely long names from filling the screen and preventing scrolling
$(this).parent().parent().find(".wisortable-body").addClass("hidden");
$(this).parent().css("max-height", "200px").find(".wifoldername").find(".form-control").css("max-height", "181px");
}).on("mouseup", function () {
$(".wisortable-excluded-dynamic").removeClass("wisortable-excluded-dynamic");
$(this).parent().parent().find(".wisortable-body").removeClass("hidden");
$(this).parent().css("max-height", "").find(".wifoldername").find(".form-control").css("max-height", "");
});
$("#btn_wifolderexpand"+uid).on("click", function () {
if($(this).hasClass("folder-expanded")) {
socket.send({'cmd': 'wifoldercollapsecontent', 'data': uid});
} else {
socket.send({'cmd': 'wifolderexpandcontent', 'data': uid});
}
})
adjustWiFolderNameHeight($("#wifoldername"+uid)[0]);
if(ob.collapsed) {
setTimeout(function() {
var container = $("#wilistfoldercontainer"+uid);
hide([container.find(".wifoldergutter-container"), container.find(".wisortable-body")]);
}, 2);
}
} else {
wi_menu.append("
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
");
$("#btn_wifolder"+uid).on("click", function () {
expandWiFolderLine(uid);
});
}
}
function expandWiLine(num) {
show([$("#wikey"+num), $("#wientry"+num), $("#wihandle"+num), $("#selective-key-"+num), $("#constant-key-"+num), $("#btn_wiselon"+num), $("#wicomment"+num)]);
$("#wihandle"+num).removeClass("wihandle-inactive").addClass("wihandle");
$("#btn_wi"+num).html("X");
$("#btn_wi"+num).off();
$("#wilistitem"+num).removeClass("wilistitem-uninitialized").removeClass("wisortable-excluded");
$("#btn_wi"+num).on("click", function () {
showWiDeleteConfirm(num);
});
adjustWiCommentHeight($("#wicomment"+num)[0]);
}
function expandWiFolderLine(num) {
socket.send({'cmd': 'wifolderinit', 'data': ''});
}
function showWiDeleteConfirm(num) {
hide([$("#btn_wi"+num)]);
show([$("#btn_widel"+num), $("#btn_wican"+num)]);
}
function showWiFolderDeleteConfirm(num) {
hide([$("#btn_wifolder"+num)]);
show([$("#btn_wifolderdel"+num), $("#btn_wifoldercan"+num)]);
}
function hideWiDeleteConfirm(num) {
show([$("#btn_wi"+num)]);
hide([$("#btn_widel"+num), $("#btn_wican"+num)]);
}
function hideWiFolderDeleteConfirm(num) {
show([$("#btn_wifolder"+num)]);
hide([$("#btn_wifolderdel"+num), $("#btn_wifoldercan"+num)]);
}
function collapseWiFolderContent(uid) {
hide([$("#wifoldergutter"+uid), $(".wisortable-body[folder-uid="+uid+"]")]);
$("#btn_wifolderexpand"+uid).removeClass("folder-expanded");
$("#wilistfoldercontainer"+uid).removeClass("folder-expanded");
}
function expandWiFolderContent(uid) {
show([$("#wifoldergutter"+uid), $(".wisortable-body[folder-uid="+uid+"]")]);
$("#btn_wifolderexpand"+uid).addClass("folder-expanded");
$("#wilistfoldercontainer"+uid).addClass("folder-expanded");
}
function enableWiSelective(num) {
hide([$("#wikey"+num)]);
$("#wikeyprimary"+num).val($("#wikey"+num).val());
show([$("#wikeyprimary"+num), $("#wikeysecondary"+num)]);
var element = $("#selective-key-"+num);
element.addClass("selective-key-icon-enabled");
$("#wikey"+num).addClass("wilistitem-selective");
}
function disableWiSelective(num) {
hide([$("#wikeyprimary"+num), $("#wikeysecondary"+num)]);
$("#wikey"+num).val($("#wikeyprimary"+num).val());
show([$("#wikey"+num)]);
var element = $("#selective-key-"+num);
element.removeClass("selective-key-icon-enabled");
$("#wikey"+num).removeClass("wilistitem-selective");
}
function enableWiConstant(num) {
var element = $("#constant-key-"+num);
element.addClass("constant-key-icon-enabled");
$("#wikey"+num).addClass("wilistitem-constant");
}
function disableWiConstant(num) {
var element = $("#constant-key-"+num);
element.removeClass("constant-key-icon-enabled");
$("#wikey"+num).removeClass("wilistitem-constant");
}
function highlightImportLine(ref) {
$("#popupcontent > div").removeClass("popuplistselected");
ref.addClass("popuplistselected");
enableButtons([popup_accept]);
}
function enableButtons(refs) {
for(i=0; i");
}
function hideWaitAnimation() {
$('#waitanim').remove();
}
function scrollToBottom() {
setTimeout(function () {
game_text.stop(true).animate({scrollTop: game_text.prop('scrollHeight')}, 500);
}, 5);
}
function hide(refs) {
for(i=0; i\
\
\
\
\
\
");
$("#sp"+i).on("click", function () {
enableButtons([sp_accept]);
socket.send({'cmd': 'spselect', 'data': $(this).attr("name")});
highlightSPLine($(this));
});
}
}
function buildUSList(unloaded, loaded) {
usunloaded.html("");
usloaded.html("");
showUSPopup();
var i;
var j;
var el = usunloaded;
var ar = unloaded;
for(j=0; j<2; j++) {
for(i=0; i\
\
\
"+ar[i].modulename+"
\
<"+ar[i].filename+">
\
\
\
"+ar[i].description+"
\
\
\
");
}
el = usloaded;
ar = loaded;
}
}
function highlightLoadLine(ref) {
$("#loadlistcontent > div > div.popuplistselected").removeClass("popuplistselected");
ref.addClass("popuplistselected");
}
function highlightSPLine(ref) {
$("#splistcontent > div > div.popuplistselected").removeClass("popuplistselected");
ref.addClass("popuplistselected");
}
function showNewStoryPopup() {
nspopup.removeClass("hidden");
nspopup.addClass("flex");
}
function hideNewStoryPopup() {
nspopup.removeClass("flex");
nspopup.addClass("hidden");
}
function showRandomStoryPopup() {
rspopup.removeClass("hidden");
rspopup.addClass("flex");
if($("#setrngpersist").prop("checked")) {
$("#rngmemory").val(memorytext);
}
}
function hideRandomStoryPopup() {
rspopup.removeClass("flex");
rspopup.addClass("hidden");
}
function statFlash(ref) {
ref.addClass("status-flash");
setTimeout(function () {
ref.addClass("colorfade");
ref.removeClass("status-flash");
setTimeout(function () {
ref.removeClass("colorfade");
}, 1000);
}, 50);
}
function updateUSStatItems(items, flash) {
var stat_us = $("#stat-us");
var stat_usactive = $("#stat-usactive");
if(flash || stat_usactive.find("li").length != items.length) {
statFlash(stat_us.closest(".statusicon").add("#usiconlabel"));
}
stat_usactive.html("");
if(items.length == 0) {
stat_us.html("No userscripts active");
$("#usiconlabel").html("");
stat_us.closest(".statusicon").removeClass("active");
return;
}
stat_us.html("Active userscripts:");
stat_us.closest(".statusicon").addClass("active");
var i;
for(i = 0; i < items.length; i++) {
stat_usactive.append($("