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;
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 }); chunk_delete_observer.observe(document.getElementById('Selected Text'), { subtree: true, 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
if (anchorNode === null) { //User changes text. For this we simply mark it as dirty
anchorNode = window.getSelection().anchorNode; var game_text = document.getElementById("Selected Text");
} else { var did_deletes = false;
for (item of document.getElementsByClassName("editing")) { for (const record of records) {
item.classList.remove("editing"); if ((record.type === "childList") && (record.removedNodes.length > 0)) {
}; for (const chunk of record.removedNodes) {
} if ((chunk instanceof HTMLElement) && (chunk.hasAttribute("chunk"))) {
chunk.innerText = '';
//OK we know where our cursor is at. Let's see if we can figure out what's changed var found = -1
for (let i = parseInt(chunk.getAttribute("chunk"))-1; i > -1; i--) {
//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
while ((!(anchorNode instanceof HTMLElement) || !anchorNode.hasAttribute('chunk')) && (anchorNode.id != 'Selected Text')) { if (document.getElementById("Selected Text Chunk " + i)) {
anchorNode = anchorNode.parentElement; found = i;
} break;
}
//If we went all the way up to the selected text node then we've killed all the game text. Treat that different }
if (anchorNode.id == 'Selected Text') { if (found != -1) {
update_game_text(-1, anchorNode.innerText); if (document.getElementById("Selected Text Chunk " + found).nextSibling) {
//We have a chunk. Check to see if it changed. If it didn't then there isn't anything to do document.getElementById("Selected Text Chunk " + found).parentNode.insertBefore(chunk, document.getElementById("Selected Text Chunk " + found).nextSibling);
} else if (anchorNode.innerText != anchorNode.original_text) { } else {
Initial_Game_Chunk = parseInt(anchorNode.getAttribute("chunk")) document.getElementById("Selected Text Chunk " + found).parentNode.append(chunk);
console.log("Changing action "+Initial_Game_Chunk+" due to change (initial selection)"); }
console.log(anchorNode.innerText); } else if (parseInt(chunk.getAttribute("chunk")) == -1) {
console.log(anchorNode.original_text); game_text.prepend(chunk);
update_game_text(Initial_Game_Chunk, anchorNode.innerText); } else {
game_text.append(chunk);
//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; chunk.classList.add("dirty");
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)) { did_delets = true;
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; } else {
var chunk = record.target;
var skip = true;
while (chunk != game_text) {
if ((chunk instanceof HTMLElement) && (chunk.hasAttribute("chunk"))) {
skip = false;
break;
}
chunk = chunk.parentNode;
}
if ((chunk.original_text != chunk.innerText) && (!skip)) {;
chunk.classList.add("dirty");
} else if ((skip) && (record.target.childNodes.length > 1) && !(record.target.childNodes[1] instanceof HTMLElement) && (record.target.childNodes[1].data.trim() != "")) {
//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 -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
if (document.getElementById("story_prompt") && (document.getElementById("story_prompt").innerText != document.getElementById("story_prompt").original_text)) {
update_game_text(-1, document.getElementById("story_prompt").innerText);
} else if (!document.getElementById("story_prompt")) {
update_game_text(-1, '');
}
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 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 --------------------->