diff --git a/static/application.js b/static/application.js index e8f3d4e9..85ab88a8 100644 --- a/static/application.js +++ b/static/application.js @@ -85,6 +85,7 @@ var wiscroll = 0; var editmode = false; var connected = false; var newly_loaded = true; +var all_modified_chunks = new Set(); var modified_chunks = new Set(); var empty_chunks = new Set(); var gametext_bound = false; @@ -126,6 +127,7 @@ var adventure = false; var chatmode = false; var sliders_throttle = getThrottle(200); +var submit_throttle = null; //=================================================================// // METHODS @@ -882,6 +884,17 @@ function dosubmit(disallow_abort) { return; } chunkOnFocusOut("override"); + // Wait for editor changes to be applied before submitting + submit_throttle = getThrottle(70); + submit_throttle.txt = txt; + submit_throttle.disallow_abort = disallow_abort; + submit_throttle(0, _dosubmit); +} + +function _dosubmit() { + var txt = submit_throttle.txt; + var disallow_abort = submit_throttle.disallow_abort; + submit_throttle = null; input_text.val(""); hideMessage(); hidegenseqs(); @@ -1415,14 +1428,30 @@ function chunkOnTextInput(event) { r.deleteContents(); } - // In Chrome the added
will go outside of the chunks if we press + // In Chrome and Safari the added
will go outside of the chunks if we press // enter at the end of the story in the editor, so this is here // to put the
back in the right place var br = $("#_EDITOR_LINEBREAK_")[0]; if(br.parentNode === game_text[0]) { + var parent = br.previousSibling; if(br.previousSibling.nodeType !== 1) { + parent = br.previousSibling.previousSibling; br.previousSibling.previousSibling.appendChild(br.previousSibling); } + if(parent.lastChild.tagName === "BR") { + parent.lastChild.remove(); // Chrome and Safari also insert an extra
in this case for some reason so we need to remove it + if(using_webkit_patch) { + // Safari on iOS has a bug where it selects all text in the last chunk of the story when this happens so we collapse the selection to the end of the chunk in that case + setTimeout(function() { + var s = getSelection(); + var r = s.getRangeAt(0); + r.selectNodeContents(parent); + r.collapse(false); + s.removeAllRanges(); + s.addRange(r); + }, 2); + } + } br.previousSibling.appendChild(br); r.selectNodeContents(br.parentNode); s.removeAllRanges(); @@ -1604,6 +1633,7 @@ function applyChunkDeltas(nodes) { var chunks = Array.from(buildChunkSetFromNodeArray(nodes)); for(var i = 0; i < chunks.length; i++) { modified_chunks.add(chunks[i]); + all_modified_chunks.add(chunks[i]); } setTimeout(function() { var chunks = Array.from(modified_chunks); @@ -1614,12 +1644,18 @@ function applyChunkDeltas(nodes) { if(!selected_chunks.has(chunks[i])) { modified_chunks.delete(chunks[i]); socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': formatChunkInnerText(chunk)}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } } empty_chunks.delete(chunks[i]); } else { if(!selected_chunks.has(chunks[i])) { modified_chunks.delete(chunks[i]); socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': formatChunkInnerText(chunk)}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } } empty_chunks.add(chunks[i]); } @@ -1641,6 +1677,9 @@ function syncAllModifiedChunks(including_selected_chunks=false) { empty_chunks.delete(chunks[i]); } socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': data}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } } } } @@ -1693,10 +1732,16 @@ function restorePrompt() { if(this.innerText.trim().length) { saved_prompt = this.innerText.trim(); socket.send({'cmd': 'inlinedelete', 'data': this.getAttribute("n")}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } this.parentNode.removeChild(this); return false; } socket.send({'cmd': 'inlinedelete', 'data': this.getAttribute("n")}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } this.parentNode.removeChild(this); }); } @@ -1711,6 +1756,9 @@ function restorePrompt() { modified_chunks.delete('0'); empty_chunks.delete('0'); socket.send({'cmd': 'inlineedit', 'chunk': '0', 'data': saved_prompt}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } } function deleteEmptyChunks() { @@ -1722,13 +1770,21 @@ function deleteEmptyChunks() { restorePrompt(); } else { socket.send({'cmd': 'inlinedelete', 'data': chunks[i]}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } } } if(modified_chunks.has('0')) { modified_chunks.delete(chunks[i]); socket.send({'cmd': 'inlineedit', 'chunk': chunks[i], 'data': formatChunkInnerText(document.getElementById("n0"))}); + if(submit_throttle !== null) { + submit_throttle(0, _dosubmit); + } + } + if(gamestarted) { + saved_prompt = formatChunkInnerText($("#n0")[0]); } - saved_prompt = formatChunkInnerText($("#n0")[0]); } function highlightEditingChunks() { @@ -1752,11 +1808,29 @@ function highlightEditingChunks() { } function cleanupChunkWhitespace() { + unbindGametext(); + + var chunks = Array.from(all_modified_chunks); + for(var i = 0; i < chunks.length; i++) { + var original_chunk = document.getElementById("n" + chunks[i]); + if(original_chunk === null || original_chunk.innerText.trim().length === 0) { + all_modified_chunks.delete(chunks[i]); + modified_chunks.delete(chunks[i]); + empty_chunks.add(chunks[i]); + } + } + // Merge empty chunks with the next chunk var chunks = Array.from(empty_chunks); chunks.sort(function(e) {parseInt(e)}); for(var i = 0; i < chunks.length; i++) { + if(chunks[i] == "0") { + continue; + } var original_chunk = document.getElementById("n" + chunks[i]); + if(original_chunk === null) { + continue; + } var chunk = original_chunk.nextSibling; while(chunk) { if(chunk.tagName === "CHUNK") { @@ -1766,11 +1840,14 @@ function cleanupChunkWhitespace() { } if(chunk) { chunk.innerText = original_chunk.innerText + chunk.innerText; + if(original_chunk.innerText.length != 0 && !modified_chunks.has(chunk.getAttribute("n"))) { + modified_chunks.add(chunk.getAttribute("n")); + } } original_chunk.innerText = ""; } // Move whitespace at the end of non-empty chunks into the beginning of the next non-empty chunk - var chunks = Array.from(modified_chunks); + var chunks = Array.from(all_modified_chunks); chunks.sort(function(e) {parseInt(e)}); for(var i = 0; i < chunks.length; i++) { var original_chunk = document.getElementById("n" + chunks[i]); @@ -1784,9 +1861,14 @@ function cleanupChunkWhitespace() { var ln = original_chunk.innerText.trimEnd().length; if (chunk) { chunk.innerText = original_chunk.innerText.substring(ln) + chunk.innerText; + if(ln != original_chunk.innerText.length && !modified_chunks.has(chunk.getAttribute("n"))) { + modified_chunks.add(chunk.getAttribute("n")); + } } original_chunk.innerText = original_chunk.innerText.substring(0, ln); } + + bindGametext(); } // This gets run every time the text in a chunk is edited @@ -1868,6 +1950,7 @@ function chunkOnFocusOut(event) { return; } cleanupChunkWhitespace(); + all_modified_chunks = new Set(); syncAllModifiedChunks(true); setTimeout(function() { var blurred = game_text[0] !== document.activeElement; @@ -2039,6 +2122,7 @@ $(document).ready(function(){ unbindGametext(); allowedit = gamestarted && $("#allowediting").prop('checked'); game_text.attr('contenteditable', allowedit); + all_modified_chunks = new Set(); modified_chunks = new Set(); empty_chunks = new Set(); game_text.html(msg.data); @@ -2495,6 +2579,12 @@ $(document).ready(function(){ chunkOnFocusOut ); mutation_observer = new MutationObserver(chunkOnDOMMutate); + $("#gamescreen").on('click', function(e) { + if(this !== e.target) { + return; + } + document.activeElement.blur(); + }); // This is required for the editor to work correctly in Firefox on desktop // because the gods of HTML and JavaScript say so