diff --git a/assets/js/icecoder.js b/assets/js/icecoder.js index 77d4a53..b15cbdd 100644 --- a/assets/js/icecoder.js +++ b/assets/js/icecoder.js @@ -27,7 +27,9 @@ var ICEcoder = { cMInstances: [], // List of CodeMirror instance no's nextcMInstance: 1, // Next available CodeMirror instance no selectedFiles: [], // Array of selected files - findMode: false, // States if we're in find/replace mode + results: [], // Array of find coords (line & char) + resultsLines: [], // Array of lines containing results (simpler version of results) + findResult: 0, // Array position of current find in results scrollbarVisible: false, // Indicates if the main pane has a scrollbar mouseDown: false, // If the mouse is down mouseDownInCM: false, // If the mouse is down within CodeMirror instance (can be false, 'editor' or 'gutter') @@ -414,12 +416,15 @@ var ICEcoder = { // On key up cMonKeyUp: function(thisCM, cMinstance) { - if ("undefined" !== typeof doFind) { - clearInterval(doFind); + if (undefined !== typeof this.doFindInt) { + clearInterval(this.doFindInt); + } + // If we have something to find in this document, find in 50 ms (unless cancelled by another keypress) + if ("" !== get('find').value && t['this document'] === document.findAndReplace.target.value) { + this.doFindInt = setTimeout(function (ic) { + ic.findReplace(get('find').value, false, false, false); + }, 50, this); } - doFind = setTimeout(function(ic) { - ic.findReplace(get('find').value, true, false); - }, 500, this); this.setEditorStats(); }, @@ -571,12 +576,6 @@ var ICEcoder = { this.oppTagReplaceData = []; this.setEditorStats(); - if (this.findMode) { - this.results.splice(this.findResult, 1); - get('results').innerHTML = this.results.length + " " + t['results']; - this.findMode = false; - } - // Update the list of functions and classes this.updateFunctionClassList(); filepath = this.openFiles[this.selectedTab - 1]; @@ -1332,7 +1331,7 @@ var ICEcoder = { "class " + tokenString ]; for (let i = 0; i < defVars.length; i++) { - if (this.findReplace(defVars[i], false, false)) { + if (this.findReplace(defVars[i], false, false, false)) { break; } } @@ -2531,29 +2530,6 @@ var ICEcoder = { this.mouseDownInCM = false; }, - - - - - - - - - - - - - - - - - - - - - - - // ============== // FIND & REPLACE // ============== @@ -2567,178 +2543,183 @@ var ICEcoder = { ? "inline-block" : "none"; }, - findReplaceKeyUp: function() { - // Realtime finding - only action for finding in current doc - if ("in" === document.findAndReplace.connector.value && "this document" === document.findAndReplace.target.value) { - ICEcoder.findReplace(get('find').value, true, false, event.keyCode == 27); - get('findReplaceSubmit').click(); + findReplaceOnInput: function() { + // Realtime finding - only action for finding/replacing in current doc + if ("" !== get('find').value && t['this document'] === document.findAndReplace.target.value) { + ICEcoder.findReplace(get('find').value, true, false, false); get("find").focus(); + return false; } }, // Find & replace text according to user selections - findReplace: function(findString,resultsOnly,buttonClick,isCancel,findPrevious) { - var find, replace, results, thisCM, content, cursor, avgBlockH, addPadding, rBlocks, blockColor, replaceQS, targetQS, filesQS; + findReplace: function(find, selectNext, canActionChanges, findPrevious) { + let replace, results, thisCM, avgBlockH, addPadding, rBlocks, haveMatch, blockColor, replaceQS, targetQS, filesQS; - if (isCancel){ - // Deselect by setting value to itself, then focus on editor - get('find').value = get('find').value; - this.focus(); - return; - } - // Set findPrevious to false if not passed in - if ("undefined" == typeof findPrevious) { - findPrevious = false; - } - - // Determine our find & replace strings and results display - find = findString.toLowerCase(); + // Determine our find rExp, replace value and results display + const rExp = new RegExp(find.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), "gi"); replace = get('replace').value; results = get('results'); - // If we have something to find in currrent document + // Get CM pane thisCM = this.getThisCM(); - if (thisCM && find.length>0 && document.findAndReplace.target.value==t['this document']) { - content = thisCM.getValue().toLowerCase(); - // Find & replace the next instance, or all? - if (document.findAndReplace.connector.value==t['and'] && buttonClick) { - if (document.findAndReplace.replaceAction.value==t['replace'] && thisCM.getSelection().toLowerCase()==find) { - thisCM.replaceSelection(replace,"around"); - } else if (document.findAndReplace.replaceAction.value==t['replace all']) { - var rExp = new RegExp(find,"gi"); - thisCM.setValue(thisCM.getValue().replace(rExp,replace)); + if (thisCM && 0 < find.length && t['this document'] === document.findAndReplace.target.value) { + // Replacing? + if (t['and'] === document.findAndReplace.connector.value && true === canActionChanges) { + // Find & replace the next instance, or all? + if (t['replace'] === document.findAndReplace.replaceAction.value && thisCM.getSelection().toLowerCase() === find.toLowerCase()) { + thisCM.replaceSelection(replace, "around"); + } else if (t['replace all'] === document.findAndReplace.replaceAction.value) { + thisCM.setValue(thisCM.getValue().replace(rExp, replace)); } } - // Get the content again, as it might of changed - content = thisCM.getValue().toLowerCase(); - if (!this.findMode||find!=this.lastsearch) { - this.results = []; - this.resultsLines = []; + // Set results, resultsLines and findResult back to defaults + this.results = []; + this.resultsLines = []; + this.findResult = 0; - for (var i=0;i'; + } + }); - // Also remember the last search term made - this.lastsearch = find; + // Increment findResult one more if our selection is what we want to find and we want to find next + if (find.toLowerCase() === thisCM.getSelection().toLowerCase() && false === findPrevious) { + ICEcoder.findResult++; + } + + if (findPrevious) { + // Find & replace backwards using previous button = 1, else just find = 1 + this.findResult -= true === canActionChanges ? 1 : 2; } // If we have results if (this.results.length>0) { // Show results only - if (resultsOnly) { + if (false === selectNext) { results.innerHTML = this.results.length + " results"; - // We need to take action instead + // We may want to take action instead } else { - // Find our cursor position relative to results - // Go next - if (!findPrevious) { - this.findResult = 0; - for (var i=0;i=0;i--) { - if (this.results[i]>thisCM.indexFromPos({"ch": thisCM.getCursor().ch-1, "line": thisCM.getCursor().line})) { - this.findResult--; - } - } - } - - // Loop round to start - if (!findPrevious && this.findResult>this.results.length-1) { + // Looking for next and hit end, loop round to start + if (false === findPrevious && this.findResult > this.results.length - 1) { this.findResult = 0 } - // Loop round to end - if (findPrevious && this.findResult==1) { - this.findResult = this.results.length+1; + // Looking for previous and hit start, loop round to end + if (findPrevious && 0 > this.findResult) { + this.findResult = this.results.length - 1; + } + + // If we somehow ended up with a number under 0, set to 0 + if (this.findResult < 0) { + this.findResult = 0; } // Update results display - results.innerHTML = "Highlighted result "+(this.findResult+(!findPrevious ? 1 : -1))+" of "+this.results.length+" results"; + results.innerHTML = "Highlighted result " + (this.findResult + 1) + " of " + this.results.length + " results"; - // Now actually perform the movement in the editor - if (!findPrevious) { - // Find next instance - cursor = thisCM.getSearchCursor(find,{"ch": thisCM.getCursor().ch+1, "line": thisCM.getCursor().line},true); - cursor.findNext(); - // Find next from start of doc - if (!cursor.from()) { - cursor = thisCM.getSearchCursor(find,{line:0,ch:0},true); - cursor.findNext(); - } - } else { - // Find previous instance - cursor = thisCM.getSearchCursor(find,{"ch": thisCM.getCursor().ch-1, "line": thisCM.getCursor().line},true); - cursor.findPrevious(); - // Find previous from end of doc - if (!cursor.from()) { - cursor = thisCM.getSearchCursor(find,{line:1000000,ch:1000000},true); - cursor.findPrevious(); - } - } - // Finally, highlight our selection - thisCM.setSelection(cursor.from(), cursor.to()); + // Scroll to that line in the editor + this.goToLine(this.results[this.findResult][0], this.results[this.findResult][1], true); + + // Finally, highlight our selection and focus on CM pane + thisCM.setSelection( + {"line": this.results[this.findResult][0]-1, "ch": this.results[this.findResult][1]}, + {"line": this.results[this.findResult][0]-1, "ch": this.results[this.findResult][1] + find.length} + ); this.focus(); - this.findMode = true; } // Display the find results bar - // The avg block is either line height or fraction of space available - avgBlockH = !this.scrollBarVisible ? thisCM.defaultTextHeight() : parseInt(this.content.style.height,10)/thisCM.lineCount(); - // Need to add padding if there's no scrollbar, so current line highlighting lines up with it - addPadding = !this.scrollBarVisible ? thisCM.heightAtLine(0) : 0; - rBlocks = ""; - for (var i=1; i<=thisCM.lineCount(); i++) { - blockColor = this.resultsLines.indexOf(i)>-1 ? thisCM.getCursor().line+1 == i ? "rgba(192,0,0,0.3)" : "rgba(128,128,128,0.3)" : "transparent"; - rBlocks += '
'; - } this.content.contentWindow.document.getElementById('resultsBar').innerHTML = rBlocks; this.content.contentWindow.document.getElementById('resultsBar').style.display = "inline-block"; + return true; } else { results.innerHTML = "No results"; this.content.contentWindow.document.getElementById('resultsBar').innerHTML = ""; this.content.contentWindow.document.getElementById('resultsBar').style.display = "none"; + return false; } } else { // Show the relevant multiple results popup - if (find != "" && buttonClick) { + if (find !== "" && true === canActionChanges) { + // Set replace, target and files query string to empty replaceQS = ""; targetQS = ""; filesQS = ""; - if (document.findAndReplace.connector.value==t['and']) { - replaceQS = "&replace="+replace; + + // Replacing? + if (t['and'] === document.findAndReplace.connector.value) { + replaceQS = "&replace=" + replace; } - if (document.findAndReplace.target.value.indexOf(t['file'])>=0) { - targetQS = "&target="+document.findAndReplace.target.value.replace(/ /g,"-"); + // Target? + + if (0 <= document.findAndReplace.target.value.indexOf(t['file'])) { + targetQS = "&target=" + document.findAndReplace.target.value.replace(/ /g, "-"); } - if (document.findAndReplace.target.value==t['selected files']) { + + // Files? + if (t['selected files'] === document.findAndReplace.target.value) { filesQS = "&selectedFiles="+this.selectedFiles.join(":"); } + + // Establish find find = find.replace(/\'/g, '\''); - find != encodeURIComponent(find) ? find = 'ICEcoder:'+encodeURIComponent(find) : find; + find !== encodeURIComponent(find) ? find = 'ICEcoder:' + encodeURIComponent(find) : find; + + // Finally, show loading mask and open multiple results pane using QS params this.showHide('show',get('loadingMask')); - get('mediaContainer').innerHTML = ''; - // We have nothing to search for, blank it all out + get('mediaContainer').innerHTML = ''; + // We have nothing to search for, blank it all out } else { results.innerHTML = "No results"; this.content.contentWindow.document.getElementById('resultsBar').innerHTML = ""; @@ -2748,11 +2729,39 @@ var ICEcoder = { }, // Replace text in a file - replaceInFile: function(fileRef,find,replace) { - this.serverQueue("add",iceLoc+"/lib/file-control.php?action=replaceText&find="+find+"&replace="+replace+"&csrf="+this.csrf,encodeURIComponent(fileRef.replace(/\//g,"|"))); - this.serverMessage(''+t['Replacing text in']+'
'+fileRef); + replaceInFile: function(fileRef, find, replace) { + this.serverQueue( + "add", + iceLoc + + "/lib/file-control.php?action=replaceText&find=" + find + + "&replace=" + replace + + "&csrf=" + this.csrf, + encodeURIComponent(fileRef.replace(/\//g, "|"))); + this.serverMessage('' + t['Replacing text in'] + '
' + fileRef); }, + + + + + + + + + + + + + + + + + + + + + + // ============== // INFO & DISPLAY // ============== @@ -3522,8 +3531,8 @@ var ICEcoder = { // Update and show/hide found results display? updateResultsDisplay: function(showHide) { - this.findReplace(get('find').value,true,false); - get('results').style.display = showHide=="show" ? 'inline-block' : 'none'; + this.findReplace(get('find').value, false, false, false); + get('results').style.display = "show" === showHide ? 'inline-block' : 'none'; }, // Toggle full screen on/off @@ -3840,16 +3849,13 @@ var ICEcoder = { // Highlight the selected tab this.redoTabHighlight(this.selectedTab); - // Redo our find display - this.findMode = false; - this.findReplace(get('find').value,true,false); - // Update our versions display this.updateVersionsDisplay(); // Detect if we have a scrollbar & set layout again setTimeout(function(ic) { ic.scrollBarVisible = thisCM.getScrollInfo().height > thisCM.getScrollInfo().clientHeight; + ic.findReplace(get('find').value, false, false, false); ic.setLayout(); },0,this); @@ -4396,14 +4402,8 @@ var ICEcoder = { // that find has focus and refuses to give it focus second time. get('goToLineNo').focus(); find.focus(); - // Trigger the find/replace operation - if(key==70) { - // Find next - get('findReplaceSubmit').click(); - } else { - // Find previous - this.findReplace(document.getElementById('find').value,false,true,false,'findPrevious'); - } + // Trigger the find/replace operation (70 = F (next), 71 = G (prev)) + this.findReplace(find.value, true, true, 70 !== key); return false; // CTRL/Cmd+L (Go to line)