New edit savings. Based on mutation observers. Needs edit highlighting

This commit is contained in:
ebolam
2023-04-19 21:19:16 -04:00
parent 34f4a03f02
commit 05ce5dcc27
2 changed files with 75 additions and 143 deletions

View File

@@ -42,34 +42,9 @@ socket.on("generated_wi", showGeneratedWIData);
initalizeTooltips(); initalizeTooltips();
//setup an observer on the game text //setup an observer on the game text
var chunk_delete_observer = new MutationObserver(function (records) { var chunk_delete_observer = new MutationObserver(function (records) {gametextwatcher(records)});
for (const record of records) {
if ((record.type === "childList") && (record.removedNodes.length > 0)) {
for (const chunk of record.removedNodes) {
if (chunk instanceof HTMLElement) {
if (chunk.id == "story_prompt") {
chunk.innerText = '';
document.getElementById("Selected Text").prepend(chunk);
}
update_game_text(parseInt(chunk.getAttribute("chunk")), '');
}
}
} else if ((record.type === "childList") && (record.addedNodes.length > 0) && !(record.addedNodes[0] instanceof HTMLElement)) {
//If we delete everything then start typing it creates the text as a direct element to the game text node. This is not in a chunk, so let's fix that
story_prompt = document.getElementById("story_prompt")
if (!story_prompt.lastChild) {
story_prompt.append(document.createElement("span"));
}
story_prompt.lastChild.innerText = record.addedNodes[0].data; chunk_delete_observer.observe(document.getElementById('Selected Text'), { subtree: true, childList: true, characterData: true });
update_game_text(parseInt(-1), record.addedNodes[0].data);
setTimeout(function () {put_cursor_at_element(story_prompt);}, 500);
record.addedNodes[0].remove();
}
}
});
chunk_delete_observer.observe(document.getElementById('Selected Text'), { childList: true, characterData: true });
var vars_sync_time = {}; var vars_sync_time = {};
var presets = {}; var presets = {};
@@ -590,13 +565,7 @@ function do_story_text_updates(action) {
item.id = 'Selected Text Chunk '+action.id; item.id = 'Selected Text Chunk '+action.id;
item.classList.add("rawtext"); item.classList.add("rawtext");
item.setAttribute("chunk", action.id); item.setAttribute("chunk", action.id);
item.addEventListener("blur", (event) => { item.setAttribute("tabindex", parseInt(action.id)+1);
select_game_text(null, event.target, 'onblur');
});
item.addEventListener("focus", (event) => {
set_edit(event.target);
});
item.setAttribute("tabindex", action.id+1);
//need to find the closest element //need to find the closest element
closest_element = document.getElementById("story_prompt"); closest_element = document.getElementById("story_prompt");
@@ -614,6 +583,8 @@ function do_story_text_updates(action) {
} }
} }
item.classList.remove("dirty");
if (action.action['Selected Text'].charAt(0) == ">") { if (action.action['Selected Text'].charAt(0) == ">") {
item.classList.add("action_mode_input"); item.classList.add("action_mode_input");
} else { } else {
@@ -703,6 +674,7 @@ function do_prompt(data) {
document.getElementById('themerow').classList.remove("hidden"); document.getElementById('themerow').classList.remove("hidden");
addInitChatMessage(); addInitChatMessage();
} }
document.getElementById("story_prompt").classList.remove("dirty");
} }
@@ -3077,72 +3049,76 @@ function set_edit(element) {
element.classList.add("editing"); element.classList.add("editing");
} }
function select_game_text(event, anchorNode = null, message= null) { function gametextwatcher(records) {
if (!((event === null) || ["ArrowRight", "ArrowLeft", "ArrowDown", "ArrowUp"].includes(event.code))) return; //Here we want to take care of two possible events
//User deleted an action. For this we'll restore the action and set it's text to "" and mark it as dirty
//User changes text. For this we simply mark it as dirty
var game_text = document.getElementById("Selected Text");
var did_deletes = false;
for (const record of records) {
if ((record.type === "childList") && (record.removedNodes.length > 0)) {
for (const chunk of record.removedNodes) {
if ((chunk instanceof HTMLElement) && (chunk.hasAttribute("chunk"))) {
chunk.innerText = '';
var found = -1
for (let i = parseInt(chunk.getAttribute("chunk"))-1; i > -1; i--) {
if (anchorNode === null) { if (document.getElementById("Selected Text Chunk " + i)) {
anchorNode = window.getSelection().anchorNode; found = i;
break;
}
}
if (found != -1) {
if (document.getElementById("Selected Text Chunk " + found).nextSibling) {
document.getElementById("Selected Text Chunk " + found).parentNode.insertBefore(chunk, document.getElementById("Selected Text Chunk " + found).nextSibling);
} else { } else {
for (item of document.getElementsByClassName("editing")) { document.getElementById("Selected Text Chunk " + found).parentNode.append(chunk);
item.classList.remove("editing");
};
} }
} else if (parseInt(chunk.getAttribute("chunk")) == -1) {
//OK we know where our cursor is at. Let's see if we can figure out what's changed game_text.prepend(chunk);
} else {
//First we need to get up to the chunk since the anchor node can be the actual text, or the span that has the world info text in it game_text.append(chunk);
while ((!(anchorNode instanceof HTMLElement) || !anchorNode.hasAttribute('chunk')) && (anchorNode.id != 'Selected Text')) {
anchorNode = anchorNode.parentElement;
} }
chunk.classList.add("dirty");
//If we went all the way up to the selected text node then we've killed all the game text. Treat that different did_delets = true;
if (anchorNode.id == 'Selected Text') {
update_game_text(-1, anchorNode.innerText);
//We have a chunk. Check to see if it changed. If it didn't then there isn't anything to do
} else if (anchorNode.innerText != anchorNode.original_text) {
Initial_Game_Chunk = parseInt(anchorNode.getAttribute("chunk"))
console.log("Changing action "+Initial_Game_Chunk+" due to change (initial selection)");
console.log(anchorNode.innerText);
console.log(anchorNode.original_text);
update_game_text(Initial_Game_Chunk, anchorNode.innerText);
//Now we need to go forward and backwards to see if anything around the selected chunk also changed (since we can select multiple actions)
current_action = Initial_Game_Chunk - 1;
while (document.getElementById("Selected Text Chunk " + current_action) && (document.getElementById("Selected Text Chunk " + current_action).innerText != document.getElementById("Selected Text Chunk " + current_action).original_text)) {
console.log("Changing action "+current_action+" due to new text");
update_game_text(current_action, document.getElementById("Selected Text Chunk " + current_action).innerText);
current_action -= 1;
} }
//We might have deleted elements, so we need to make sure we went all the way back to -1 (the prompt)
while ((current_action >= 0) && !document.getElementById("Selected Text Chunk " + current_action)) {
console.log("Deleting action "+current_action+" due to it being deleted");
update_game_text(current_action, '');
current_action -= 1;
} }
//Check the prompt } else {
if (document.getElementById("story_prompt") && (document.getElementById("story_prompt").innerText != document.getElementById("story_prompt").original_text)) { var chunk = record.target;
update_game_text(-1, document.getElementById("story_prompt").innerText); var skip = true;
} else if (!document.getElementById("story_prompt")) { while (chunk != game_text) {
update_game_text(-1, ''); if ((chunk instanceof HTMLElement) && (chunk.hasAttribute("chunk"))) {
skip = false;
break;
} }
chunk = chunk.parentNode;
current_action = Initial_Game_Chunk + 1; }
while (document.getElementById("Selected Text Chunk " + current_action) && (document.getElementById("Selected Text Chunk " + current_action).innerText != document.getElementById("Selected Text Chunk " + current_action).original_text)) { if ((chunk.original_text != chunk.innerText) && (!skip)) {;
console.log("Changing action "+current_action+" due to new text"); chunk.classList.add("dirty");
update_game_text(current_action, document.getElementById("Selected Text Chunk " + current_action).innerText); } else if ((skip) && (record.target.childNodes.length > 1) && !(record.target.childNodes[1] instanceof HTMLElement) && (record.target.childNodes[1].data.trim() != "")) {
current_action += 1; //here we added a node that wasn't under a chunk. This should only happen if you delete everything, so let's move this to the story chunk
var story_prompt = document.getElementById("story_prompt");
if (story_prompt.firstChild) {
story_prompt.firstChild.innerText = record.target.childNodes[1].data;
} else {
story_prompt.innerText = record.target.childNodes[1].data;
}
record.target.childNodes[1].remove()
story_prompt.classList.add("dirty");
put_cursor_at_element(story_prompt);
} }
//We might have deleted elements, so we need to make sure we went all the way back to the current action
while ((current_action <= current_chunk_number) && !document.getElementById("Selected Text Chunk " + current_action)) {
console.log("Deleting action "+current_action+" due to it being deleted");
update_game_text(current_action, '');
current_action += 1;
} }
} }
} }
function savegametextchanges() {
console.log("Firing save")
for (const chunk of document.getElementsByClassName("dirty")) {
update_game_text(parseInt(chunk.getAttribute("chunk")), chunk.innerText);
}
}
function capturegametextpaste(event) { function capturegametextpaste(event) {
//Stop all paste stuff from happening in the browser //Stop all paste stuff from happening in the browser
event.preventDefault(); event.preventDefault();
@@ -3169,49 +3145,6 @@ function capturegametextpaste(event) {
} }
function edit_game_text(id) {
update_game_text(id)
//OK, now we need to go backwards and forwards until we find something that didn't change
let check_id = id-1;
while (check_id >= 0) {
if (document.getElementById("Selected Text Chunk " + check_id)) {
let temp = document.getElementById("Selected Text Chunk " + check_id);
if (temp.textContent == temp.original_text) {
break;
} else {
update_game_text(check_id);
}
} else {
update_game_text(check_id);
}
check_id -= 1;
}
if (document.getElementById("story_prompt")) {
let temp = document.getElementById("story_prompt");
if (temp.textContent != temp.original_text) {
update_game_text(-1);
}
} else {
update_game_text(-1);
}
check_id = id+1;
while (check_id <= Math.max.apply(null,Object.keys(actions_data).map(Number))) {
if (document.getElementById("Selected Text Chunk " + check_id)) {
let temp = document.getElementById("Selected Text Chunk " + check_id);
if (temp.textContent == temp.original_text) {
break;
} else {
update_game_text(check_id);
}
} else {
update_game_text(check_id);
}
check_id += 1;
}
}
function update_game_text(id, new_text) { function update_game_text(id, new_text) {
let temp = null; let temp = null;
if (id == -1) { if (id == -1) {
@@ -3245,7 +3178,7 @@ function save_preset() {
function put_cursor_at_element(element) { function put_cursor_at_element(element) {
var range = document.createRange(); var range = document.createRange();
var sel = window.getSelection(); var sel = window.getSelection();
if (element.lastChild) { if ((element.lastChild) && (element.lastChild instanceof HTMLElement)) {
range.setStart(element.lastChild, element.lastChild.innerText.length); range.setStart(element.lastChild, element.lastChild.innerText.length);
} else { } else {
range.setStart(element, element.innerText.length); range.setStart(element, element.innerText.length);

View File

@@ -53,9 +53,8 @@
<div id="welcome_text" class="var_sync_model_welcome" draggable="False"></div> <div id="welcome_text" class="var_sync_model_welcome" draggable="False"></div>
</div> </div>
<!---<div class="gametext" id="Selected Text" contenteditable=false onfocusin="select_game_text('focusin');" onfocusout="select_game_text('focusout');" onclick="select_game_text(null);" onkeyup="select_game_text(event);" onpaste="capturegametextpaste(event);">--> <div class="gametext" id="Selected Text" contenteditable=false tabindex=0 onfocusout="savegametextchanges();" onpaste="capturegametextpaste(event);">
<div class="gametext" id="Selected Text" contenteditable=false onpaste="capturegametextpaste(event);"> <span id="story_prompt" class="var_sync_story_prompt var_sync_alt_story_prompt_in_ai rawtext hidden" tabindex=0 chunk="-1"></span></div><!--don't move the /div down or it'll cause odd spacing issues in the UI--->
<span id="story_prompt" class="var_sync_story_prompt var_sync_alt_story_prompt_in_ai rawtext hidden" tabindex=0 chunk="-1" onfocus="set_edit(this);" onblur="select_game_text(null, this, 'onblur prompt');"></span></div><!--don't move the /div down or it'll cause odd spacing issues in the UI--->
</div> </div>
<!------------ Sequences ---------------------> <!------------ Sequences --------------------->