// ICE coder by Matt Pass // Free to use it for your own purposes, commercial or not, just let me know of any cool uses or customisations. :) // No warranty or liability accepted for anything, all responsibility of use is your own. // Latest version: https://github.com/mattpass/ICEcoder // Twitter: @mattpass var ICEcoder = { // Define settings filesW: 15, // Initial width of the files pane minFilesW: 15, // Min width of the files pane maxFilesW: 250, // Max width of the files pane selectedTab: 0, // The tab that's currently selected changedContent: [], // Binary array to indicate which tabs have changed ctrlKeyDown: false, // Indicates if CTRL keydown shiftKeyDown: false, // Indicates if Shift keydown delKeyDown: false, // Indicates if DEL keydown canSwitchTabs: true, // Stops switching of tabs when trying to close openFiles: [], // Array of open file URLs 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 lockedNav: false, // Nav is locked or not // Don't consider these tags as part of nesting as they're singles, JS or PHP code blocks tagNestExceptions: ["!DOCTYPE","meta","link","img","br","hr","input","script","?"], // On load, set aliases, set the layout and get the nest location init: function() { var aliasArray = ["header","files","account","filesFrame","editor","tabsBar","findBar","content","footer","nestValid","nestDisplay","charDisplay"]; ICEcoder.tD = top.document; // Create our ID aliases for (var i=0;i',''); // Then set the content in the editor & clear the history cM.setValue(content); cM.clearHistory(); } }, // Work out the nesting depth location on demand and update our display if required getNestLocation: function(updateNestDisplay) { var cM, openTag, nestCheck, startPos, tagStart, canDoTheEndTag, tagEnd, tagEndJS, fileName; cM = ICEcoder.getcMInstance(); nestCheck = cM.getValue(); // Set up array to store nest data, a var to establish if a tag is open and another to establish if we're in a code block ICEcoder.htmlTagArray = [], openTag = false, ICEcoder.codeBlock = false; // For every character from the start to our caret position for(var i=0;i<=ICEcoder.caretPos;i++) { // If we find a < tag and we're not within a tag, change the open tag state & set our start position if(nestCheck.charAt(i)=="<" && openTag==false) { openTag=true; startPos=i+1; // Get the tag name and if it's the start of a code block, set the var for that tagStart=nestCheck.substr(startPos,nestCheck.length).split(" ")[0].split(">")[0].split("\n")[0]; if (tagStart=="script"||tagStart=="?") {ICEcoder.codeBlock=true} if (tagStart!="") {ICEcoder.tagStart = tagStart} }; // If we find a > tag and we're within a tag or codeblock if(nestCheck.charAt(i)==">" && (openTag||ICEcoder.codeBlock)) { // Get the tag name tagString=nestCheck.substr(0,i); tagString=tagString.substr(tagString.lastIndexOf('<')+1,tagString.length); tagString=tagString.split(" ")[0]; ICEcoder.tagString = tagString; canDoTheEndTag=true; // Check it's not on our list of exceptions for (var j=0;j ICEcoder.minFilesW+1 ? ICEcoder.filesW -= Math.ceil((ICEcoder.filesW-ICEcoder.minFilesW)/2) : ICEcoder.filesW = ICEcoder.minFilesW; } if ((expandContract=="expand" && ICEcoder.filesW == ICEcoder.maxFilesW)||(expandContract=="contract" && ICEcoder.filesW == ICEcoder.minFilesW)) { clearInterval(ICEcoder.changeFilesInt); } // Redo the layout to match ICEcoder.setLayout(); }, // Change tabs by reloading content switchTab: function(newTab) { var cM; // Identify tab that's currently selected & show the instance ICEcoder.selectedTab = newTab; cM = ICEcoder.getcMInstance(); // Switch mode ICEcoder.switchMode(); // Set all cM instances to be hidden, then make our selected instance visable for (var i=0;i'; top.document.getElementById('tab'+(top.ICEcoder.openFiles.length)).style.display = "inline-block"; top.document.getElementById('tab'+(top.ICEcoder.openFiles.length)).innerHTML = top.ICEcoder.openFiles[top.ICEcoder.openFiles.length-1] + " " + closeTabLink; // Highlight it and state it's selected top.ICEcoder.redoTabHighlight(top.ICEcoder.openFiles.length); top.ICEcoder.selectedTab=top.ICEcoder.openFiles.length; // Add a new value ready to indicate if this content has been changed top.ICEcoder.changedContent.push(0); }, // Create a new tab for a file renameTab: function(tabNum,newName) { var closeTabLink; // Push new file into array top.ICEcoder.openFiles[tabNum] = newName; // Setup a new tab closeTabLink = ''; top.document.getElementById('tab'+tabNum).innerHTML = top.ICEcoder.openFiles[tabNum] + " " + closeTabLink; }, // Indicate if the nesting structure of the code is OK updateNestingIndicator: function () { var cM, fileName; cM = ICEcoder.getcMInstance(); fileName = ICEcoder.openFiles[ICEcoder.selectedTab-1]; ICEcoder.caretPos=cM.getValue().length; ICEcoder.getNestLocation(); // Nesting is OK if at the end of the file we have no nests left, or it's a JS or CSS file if (ICEcoder.htmlTagArray.length==0||fileName.indexOf(".js")>0||fileName.indexOf(".css")>0) { ICEcoder.nestValid.style.backgroundColor="#00bb00"; ICEcoder.nestValid.innerHTML = "Nesting OK"; } else { ICEcoder.nestValid.style.backgroundColor="#ff0000"; ICEcoder.nestValid.innerHTML = "Nesting Broken"; } }, // Get the caret position on demand getCaretPosition: function() { var cM, content, line, char, charPos, charCount; cM = ICEcoder.getcMInstance(); content = cM.getValue(); line = cM.getCursor().line; char = cM.getCursor().ch; charPos = 0; for (var i=0;icaretChunk.lastIndexOf("")&&caretLocType=="Unknown") {caretLocType = "JavaScript"}; if (caretChunk.lastIndexOf("caretChunk.lastIndexOf("?>")&&caretLocType=="Unknown") {caretLocType = "PHP"}; if (caretChunk.lastIndexOf("<")>caretChunk.lastIndexOf(">")&&caretLocType=="Unknown") {caretLocType = "HTML"}; if (caretLocType=="Unknown") {caretLocType = "Content"}; var fileName; fileName = ICEcoder.openFiles[ICEcoder.selectedTab-1]; if (fileName.indexOf(".js")>0) {caretLocType="JavaScript"}; if (fileName.indexOf(".css")>0) {caretLocType="CSS"}; ICEcoder.caretLocType = caretLocType; // If we're in a JS or PHP code block, add that to the nest display if (caretLocType=="JavaScript"||caretLocType=="PHP") { ICEcoder.nestDisplay.innerHTML += " > " + caretLocType; } }, // Alter array indicating which files have changed redoChangedContent: function(evt) { var key; key = evt.keyCode ? evt.keyCode : evt.which ? evt.which : evt.charCode; // Exclude a few keys such as Escape... if (top.ICEcoder.ctrlKeyDown==false && key!=27 && key!=20 && (key<16||key>19) && (key<37||key>40) && (key!=144||key!=145) && (key!=44||key!=45) && (key<33||key>36) && (key!=91||key!=92) && (key<112||key>123)) { ICEcoder.changedContent[ICEcoder.selectedTab-1] = 1; ICEcoder.redoTabHighlight(ICEcoder.selectedTab); } }, // Close the tab upon request closeTab: function(closeTabNum) { var cM, okToRemove; cM = ICEcoder.getcMInstance(); okToRemove = true; if (ICEcoder.changedContent[closeTabNum-1]==1) { okToRemove = confirm('You have made changes.\n\nAre you sure you want to close without saving?'); } if (okToRemove) { // recursively copy over all tabs & data from the tab to the right, if there is one for (var i=closeTabNum;i0 ? ICEcoder.selectedTab-=1 : ICEcoder.selectedTab = 0; if (ICEcoder.openFiles.length>0 && ICEcoder.selectedTab==0) {ICEcoder.selectedTab=1}; // hide the content area if we have no tabs open if (ICEcoder.openFiles.length==0) { ICEcoder.tD.getElementById('content').style.visibility = "hidden"; } else { // Switch the mode & the tab ICEcoder.switchMode(); ICEcoder.switchTab(ICEcoder.selectedTab); } // Highlight the selected tab after splicing the change state out of the array top.ICEcoder.changedContent.splice(closeTabNum-1,1); top.parent.ICEcoder.redoTabHighlight(ICEcoder.selectedTab); } // Lastly, stop it from trying to also switch tab top.ICEcoder.canSwitchTabs=false; }, // Setup the file manager fileManager: function() { ICEcoder.filesFrame = top.document.getElementById('filesFrame'); if (!ICEcoder.filesFrame.contentWindow.document.getElementsByTagName) {return;}; var aMenus = ICEcoder.filesFrame.contentWindow.document.getElementsByTagName("LI"); for (var i=0; i -1) { var submenu=aMenus[i].childNodes; for (var j=0; j -1) ? "open" : "closed"; } if (submenu[j].tagName == "UL") submenu[j].style.display = (mclass.indexOf("open") > -1) ? "block" : "none"; } } } return false; }, // Note which files or foldets we are over on mouseover/mouseout overFileFolder: function(type, link) { ICEcoder.thisFileFolderType=type; ICEcoder.thisFileFolderLink=link; }, // Select file or folder on demand selectFileFolder: function() { var resetFile, shortURL, foundSelectedFile, foundShortURL, foundFile, resetFile; // If we've clicked somewhere other than a file/folder if (top.ICEcoder.thisFileFolderLink=="") { if (!top.ICEcoder.ctrlKeyDown) { // Deselect all files for (var i=0;i<=top.ICEcoder.selectedFiles.length;i++) { if (top.ICEcoder.selectedFiles[i]) { resetFile = top.ICEcoder.filesFrame.contentWindow.document.getElementById(top.ICEcoder.selectedFiles[i]); resetFile.style.backgroundColor="#dddddd"; resetFile.style.color="#000000"; } } // Set our arrray to contain 0 items top.ICEcoder.selectedFiles.length = 0; } } else { // We clicked a file/folder. Work out a shortened URL for the file, with pipes instead of slashes shortURL = top.ICEcoder.thisFileFolderLink.substr((top.ICEcoder.thisFileFolderLink.indexOf(shortURLStarts)+top.shortURLStarts.length),top.ICEcoder.thisFileFolderLink.length).replace(/\//g,"|"); // If we have the CTRL key down if (top.ICEcoder.ctrlKeyDown) { foundSelectedFile=false; // Reset all files to not be highlighted for (i=0;i<=top.ICEcoder.selectedFiles.length;i++) { if (top.ICEcoder.selectedFiles[i]==shortURL) { resetFile = ICEcoder.filesFrame.contentWindow.document.getElementById(top.ICEcoder.selectedFiles[i]); resetFile.style.backgroundColor="#dddddd"; resetFile.style.color="#000000"; top.ICEcoder.selectedFiles.splice(i); foundSelectedFile=true; } } if (!foundSelectedFile) { foundFile = ICEcoder.filesFrame.contentWindow.document.getElementById(shortURL); foundFile.style.backgroundColor="#888888"; foundFile.style.color="#f8f8f8"; top.ICEcoder.selectedFiles.push(shortURL); } // We are single clicking } else { // First deselect all files for (i=0;i'; top.document.getElementById('tab'+(i+1)).innerHTML = top.ICEcoder.openFiles[i] + " " + closeTabLink; } } ICEcoder.filesFrame.contentWindow.frames['fileControl'].location.href = "lib/file-control.php?action=rename&file="+renamedFile+"&oldFileName="+top.ICEcoder.rightClickedFile.replace(/\|/g,"/"); } }, // Delete a file on demand deleteFile: function() { var delFiles, selectedFilesList; delFiles = confirm('Delete:\n\n'+top.ICEcoder.selectedFiles.toString().replace(/\|/g,"/").replace(/,/g,"\n")+'?'); // Upon supply a new name, rename tabs and update filename on server if (delFiles) { selectedFilesList = ""; for (var i=0;i0) { cM = ICEcoder.getcMInstance(); content = cM.getValue().toLowerCase(); // Find & replace the next instance? if (document.findAndReplace.connector.value=="and" && cM.getSelection()==find) { cM.replaceSelection(document.getElementById('replace').value); } if (!top.ICEcoder.findMode||parent.parent.document.getElementById('find').value!=ICEcoder.lastsearch) { ICEcoder.results = []; for (var i=0;i0) { // Show results only if (resultsOnly) { parent.parent.document.getElementById('results').innerHTML = ICEcoder.results.length + " results"; // We need to take action instead } else { lineCount=1; numChars=0; for (var i=0;iICEcoder.results.length-1) {ICEcoder.findResult=0}; parent.parent.document.getElementById('results').innerHTML = "Highlighted result "+(ICEcoder.findResult+1)+" of "+ICEcoder.results.length+" results"; lineCount=0; for (var i=0;i0) { cM.setOption("mode","javascript"); } else if (fileName.indexOf('.css')>0) { cM.setOption("mode","css"); } else { cM.setOption("mode","application/x-httpd-php"); } }, // Lock & unlock the file manager navigation on demand lockUnlockNav: function() { var lockIcon; lockIcon = top.document.getElementById('fmLock'); ICEcoder.lockedNav ? ICEcoder.lockedNav = false : ICEcoder.lockedNav = true; ICEcoder.lockedNav ? lockIcon.src="images/file-manager-icons/padlock.png" : lockIcon.src="images/file-manager-icons/padlock-disabled.png"; }, // Determine the CodeMirror instance we're using on demand getcMInstance: function(newTab) { var cM; if (newTab=="new") { cM = top.ICEcoder.content.contentWindow['cM'+ICEcoder.cMInstances[ICEcoder.selectedTab-1]]; } else if (ICEcoder.openFiles.length==0) { cM = top.ICEcoder.content.contentWindow['cM1']; } else { cM = top.ICEcoder.content.contentWindow['cM'+ICEcoder.cMInstances[ICEcoder.selectedTab-1]]; } return cM; }, // Start running plugin intervals according to given specifics startPluginIntervals: function(plugURL,plugTarget,plugTimer) { // For this window instances if (plugTarget=="_parent"||plugTarget=="_top"||plugTarget=="_self"||plugTarget=="") { setInterval('window.location=\''+plugURL+'\'',plugTimer*1000*60); // for pluginActions iframe instances } else if (plugTarget=="pluginActions") { setInterval('document.getElementById(\'pluginActions\').src=\''+plugURL+'\'',plugTimer*1000*60); // for _blank or named target window instances } else { setInterval('window.open(\''+plugURL+'\',\''+plugTarget+'\')',plugTimer*1000*60); } }, // Comment or uncomment line on keypress lineCommentToggle: function() { var cM, lineContent, cursorPos; cM = ICEcoder.getcMInstance(); cursorPos = cM.getCursor().ch; lineContent = cM.getLine(cM.getCursor().line); if (ICEcoder.caretLocType=="JavaScript"||ICEcoder.caretLocType=="PHP") { if (lineContent.slice(0,3)!="// ") { cM.setLine(cM.getCursor().line, "// " + lineContent); cM.setCursor(cM.getCursor().line, cursorPos+3); } else { cM.setLine(cM.getCursor().line, lineContent.slice(3,lineContent.length)); cM.setCursor(cM.getCursor().line, cursorPos-3); } } else if (ICEcoder.caretLocType=="CSS") { if (lineContent.slice(0,3)!="/* ") { cM.setLine(cM.getCursor().line, "/* " + lineContent + " */"); cM.setCursor(cM.getCursor().line, cursorPos+3); } else { cM.setLine(cM.getCursor().line, lineContent.slice(3,lineContent.length).slice(0,lineContent.length-5)); cM.setCursor(cM.getCursor().line, cursorPos-3); } } else { if (lineContent.slice(0,4)!=""); cM.setCursor(cM.getCursor().line, cursorPos+4); } else { cM.setLine(cM.getCursor().line, lineContent.slice(4,lineContent.length).slice(0,lineContent.length-9)); cM.setCursor(cM.getCursor().line, cursorPos-4); } } }, // Get the mouse position on demand getMouseXY: function(e) { var tempX, tempY, scrollTop, IE; IE = document.all ? true : false; if (!IE) {document.captureEvents(Event.MOUSEMOVE)}; if (IE) { top.ICEcoder.mouseX = event.clientX + document.body.scrollLeft; top.ICEcoder.mouseY = event.clientY + document.body.scrollTop; } else { top.ICEcoder.mouseX = e.pageX; top.ICEcoder.mouseY = e.pageY; } scrollTop = top.document.getElementById('filesFrame').contentWindow.document.body.scrollTop; top.ICEcoder.mouseY -= scrollTop; } };