/* Demonstration of embedding CodeMirror in a bigger application. The * interface defined here is a mess of prompts and confirms, and * should probably not be used in a real project. */ //var CodeMirrorUI = Class.create(); function CodeMirrorUI(place, options, mirrorOptions) { this.initialize(place, options, mirrorOptions); } CodeMirrorUI.prototype = { initialize: function(textarea, options, mirrorOptions) { var defaultOptions = { searchMode: 'popup', // other options are 'inline' and 'dialog'. The 'dialog' option needs work. imagePath: 'images/silk', path: 'js', buttons: ['search', 'undo', 'redo', 'jump', 'reindentSelection', 'reindent','about'], saveCallback: function() {}, } this.textarea = textarea this.options = options; this.setDefaults(this.options, defaultOptions); this.buttonDefs = { 'save': ["Save", "save", this.options.imagePath + "/page_save.png", this.save], 'search': ["Search/Replace", "find_replace_popup", this.options.imagePath + "/find.png", this.find_replace_popup], 'searchClose': ["Close", "find_replace_popup_close", this.options.imagePath + "/cancel.png", this.find_replace_popup_close], 'searchDialog': ["Search/Replace", "find_replace_window", this.options.imagePath + "/find.png", this.find_replace_window], 'undo': ["Undo", "undo", this.options.imagePath + "/arrow_undo.png", this.undo], 'redo': ["Redo", "redo", this.options.imagePath + "/arrow_redo.png", this.redo], 'jump': ["Jump to line #", "jump", this.options.imagePath + "/page_go.png", this.jump], 'reindentSelection': ["Reformat selection", "reindentSelect", this.options.imagePath + "/text_indent.png", this.reindentSelection], 'reindent': ["Reformat whole document", "reindent", this.options.imagePath + "/page_refresh.png", this.reindent], 'about': ["About CodeMirror-UI", "about", this.options.imagePath + "/help.png", this.about] }; //place = CodeMirror.replace(place) this.home = document.createElement("div"); this.textarea.parentNode.insertBefore(this.home, this.textarea); /*if (place.appendChild) place.appendChild(this.home); else place(this.home); */ this.self = this; var onChange = this.editorChanged.cmuiBind(this); // preserve custom onChance handler if (mirrorOptions.onChange) { mirrorOptions.onChange = function() { mirrorOptions.onChange(); onChange(); } } else { mirrorOptions.onChange = onChange; } mir = CodeMirror.fromTextArea(this.textarea, mirrorOptions); //console.log(mir); this.mirror = mir; this.initButtons(); //this.initWordWrapControl(); // CodeMirror v2 does not support word wrapping if (this.options.searchMode == 'inline') { this.initFindControl(); } else if (this.options.searchMode == 'popup') { this.initPopupFindControl(); } if (this.saveButton) this.addClass(this.saveButton,'inactive'); if (this.undoButton) this.addClass(this.undoButton,'inactive'); if (this.redoButton) this.addClass(this.redoButton,'inactive'); }, setDefaults: function(object, defaults) { for (var option in defaults) { if (!object.hasOwnProperty(option)) object[option] = defaults[option]; } }, toTextArea: function() { this.home.parentNode.removeChild(this.home); this.mirror.toTextArea(); }, initButtons: function() { this.buttonFrame = document.createElement("div"); this.buttonFrame.className = "codemirror-ui-clearfix codemirror-ui-button-frame"; this.home.appendChild(this.buttonFrame); for (var i = 0; i < this.options.buttons.length; i++) { var buttonId = this.options.buttons[i]; var buttonDef = this.buttonDefs[buttonId]; this.addButton(buttonDef[0], buttonDef[1], buttonDef[2], buttonDef[3], this.buttonFrame); } //this.makeButton("Search", "search"); //this.makeButton("Replace", "replace"); //this.makeButton("Current line", "line"); //this.makeButton("Jump to line", "jump"); //this.makeButton("Insert constructor", "macro"); //this.makeButton("Indent all", "reindent"); }, /* * This is left over from the MirrorFrame demo. * Get rid of it quick. */ /* makeButton : function(name, action){ var button = document.createElement("input"); button.type = "button"; button.value = name; this.home.appendChild(button); button.onclick = function(){ self[action].call(self); }; }, */ createFindBar: function() { var findBar = document.createElement("div"); findBar.className = "codemirror-ui-find-bar"; this.findString = document.createElement("input"); this.findString.type = "text"; this.findString.size = 8; this.findButton = document.createElement("input"); this.findButton.type = "button"; this.findButton.className = "button mainaction"; this.findButton.value = "Find"; this.findButton.onclick = function(){this.find()}.cmuiBind(this); this.connect(this.findString, "keyup", function(e){ var code = e.keyCode; if (code == 13){ this.find(this.mirror.getCursor(false)) }else{ if(!this.findString.value == ""){ this.find(this.mirror.getCursor(true)) } } this.findString.focus(); }.cmuiBind(this) ); var regLabel = document.createElement("label"); regLabel.title = "Regular Expressions" this.regex = document.createElement("input"); this.regex.type = "checkbox" this.regex.className = "codemirror-ui-checkbox" regLabel.appendChild(this.regex); regLabel.appendChild(document.createTextNode("RegEx")); var caseLabel = document.createElement("label"); caseLabel.title = "Case Sensitive" this.caseSensitive = document.createElement("input"); this.caseSensitive.type = "checkbox" this.caseSensitive.className = "codemirror-ui-checkbox" caseLabel.appendChild(this.caseSensitive); caseLabel.appendChild(document.createTextNode("A/a")); this.replaceString = document.createElement("input"); this.replaceString.type = "text"; this.replaceString.size = 8; this.connect(this.replaceString, "keyup", function(e){ var code = e.keyCode; if (code == 13){ this.replace() } }.cmuiBind(this) ); this.replaceButton = document.createElement("input"); this.replaceButton.type = "button"; this.replaceButton.className = "button"; this.replaceButton.value = "Replace"; this.replaceButton.onclick = this.replace.cmuiBind(this); var replaceAllLabel = document.createElement("label"); replaceAllLabel.title = "Replace All" this.replaceAll = document.createElement("input"); this.replaceAll.type = "checkbox" this.replaceAll.className = "codemirror-ui-checkbox" replaceAllLabel.appendChild(this.replaceAll); replaceAllLabel.appendChild(document.createTextNode("All")); findBar.appendChild(this.findString); findBar.appendChild(this.findButton); findBar.appendChild(caseLabel); findBar.appendChild(regLabel); findBar.appendChild(this.replaceString); findBar.appendChild(this.replaceButton); findBar.appendChild(replaceAllLabel); return findBar; }, initPopupFindControl: function() { var findBar = this.createFindBar(); this.popupFindWrap = document.createElement("div"); this.popupFindWrap.className = "codemirror-ui-popup-find-wrap"; this.popupFindWrap.appendChild(findBar); var buttonDef = this.buttonDefs['searchClose']; this.addButton(buttonDef[0], buttonDef[1], buttonDef[2], buttonDef[3], this.popupFindWrap); this.buttonFrame.appendChild(this.popupFindWrap); }, initFindControl: function() { var findBar = this.createFindBar(); this.buttonFrame.appendChild(findBar); }, find: function( start ) { var isCaseSensitive = this.caseSensitive.checked; if(start == null){ start = this.mirror.getCursor(); } var findString = this.findString.value; if (findString == null || findString == '') { alert('You must enter something to search for.'); return; } if (this.regex.checked) { findString = new RegExp(findString, !isCaseSensitive ? "i" : ""); } this.cursor = this.mirror.getSearchCursor(findString, start, !isCaseSensitive ); var found = this.cursor.findNext(); if (found) { this.mirror.setSelection(this.cursor.from(),this.cursor.to()) //this.cursor.select(); } else { if (confirm("No more matches. Should we start from the top?")) { this.cursor = this.mirror.getSearchCursor(findString, 0, !isCaseSensitive); found = this.cursor.findNext(); if (found) { this.mirror.setSelection(this.cursor.from(),this.cursor.to()) //this.cursor.select(); } else { alert("No matches found."); } } } }, replace: function() { var findString = this.findString.value, replaceString = this.replaceString.value, isCaseSensitive = this.caseSensitive.checked, isRegex = this.regex.checked, regFindString = isRegex ? new RegExp(findString, !isCaseSensitive ? "i" : "") : ""; if (this.replaceAll.checked) { var cursor = this.mirror.getSearchCursor(isRegex ? regFindString : findString, 0, !isCaseSensitive); while (cursor.findNext()) this.mirror.replaceRange( isRegex ? cursor.pos.match[0].replace(regFindString, replaceString) : replaceString ,cursor.from(),cursor.to()); //cursor.replace(this.replaceString.value); } else { this.mirror.replaceRange( isRegex ? this.cursor.pos.match[0].replace(regFindString, replaceString) : replaceString ,this.cursor.from(),this.cursor.to()) //this.cursor.replace(this.replaceString.value); this.find(); } }, initWordWrapControl: function() { var wrapDiv = document.createElement("div"); wrapDiv.className = "codemirror-ui-wrap" var label = document.createElement("label"); this.wordWrap = document.createElement("input"); this.wordWrap.type = "checkbox" this.wordWrap.checked = true; label.appendChild(this.wordWrap); label.appendChild(document.createTextNode("Word Wrap")); this.wordWrap.onchange = this.toggleWordWrap.cmuiBind(this); wrapDiv.appendChild(label); this.buttonFrame.appendChild(wrapDiv); }, toggleWordWrap: function() { if (this.wordWrap.checked) { this.mirror.setTextWrapping("nowrap"); } else { this.mirror.setTextWrapping(""); } }, addButton: function(name, action, image, func, frame) { var button = document.createElement("a"); //button.href = "#"; button.className = "codemirror-ui-button " + action; button.title = name; button.func = func.cmuiBind(this); button.onclick = function(event) { //alert(event.target); event.target.func(); return false; //this.self[action].call(this); //eval("this."+action)(); } .cmuiBind(this, func); var img = document.createElement("img"); img.src = image; img.border = 0; img.func = func.cmuiBind(this); button.appendChild(img); frame.appendChild(button); if (action == 'save') { this.saveButton = button; } if (action == 'undo') { this.undoButton = button; } if (action == 'redo') { this.redoButton = button; } }, classNameRegex: function(className) { var regex = new RegExp("(.*) *" + className + " *(.*)"); return regex; }, addClass: function(element, className) { if (!element.className.match(this.classNameRegex(className))) { element.className += " " + className; } }, removeClass: function(element, className) { var m = element.className.match(this.classNameRegex(className)) if (m) { element.className = m[1] + " " + m[2]; } }, editorChanged: function() { if(!this.mirror) { return } var his = this.mirror.historySize(); if (his['undo'] > 0) { this.removeClass(this.saveButton, 'inactive'); this.removeClass(this.undoButton, 'inactive'); } else { this.addClass(this.saveButton, 'inactive'); this.addClass(this.undoButton, 'inactive'); } if (his['redo'] > 0) { this.removeClass(this.redoButton, 'inactive'); } else { this.addClass(this.redoButton, 'inactive'); } //alert("undo size = " + his['undo'] + " and redo size = " + his['redo']); }, save: function() { this.options.saveCallback(); this.addClass(this.saveButton, 'inactive'); }, undo: function() { this.mirror.undo(); }, redo: function() { this.mirror.redo(); }, replaceSelection: function(newVal) { this.mirror.replaceSelection(newVal); this.searchWindow.focus(); }, raise_search_window: function() { //alert('raising window!'); this.searchWindow.focus(); }, find_replace_window: function() { if (this.searchWindow == null) { this.searchWindow = window.open(this.options.path + "find_replace.html", "mywindow", "scrollbars=1,width=400,height=350,modal=yes"); this.searchWindow.codeMirrorUI = this; } this.searchWindow.focus(); }, find_replace_popup: function() { //alert('Hello!'); this.popupFindWrap.className = "codemirror-ui-popup-find-wrap active"; this.findString.focus(); }, find_replace_popup_close: function() { //alert('Hello!'); this.popupFindWrap.className = "codemirror-ui-popup-find-wrap"; }, /* find_replace: function(){ this.find_replace = document.createElement("div"); this.find_replace.className = "codemirror-search-replace"; this.find_replace.innerHTML = "Just a test!"; this.home.appendChild(this.find_replace); }, search: function(){ var text = prompt("Enter search term:", ""); if (!text) return; var first = true; do { var cursor = this.mirror.getSearchCursor(text, first); first = false; while (cursor.findNext()) { cursor.select(); if (!confirm("Search again?")) return; } } while (confirm("End of document reached. Start over?")); }, replace: function(){ // This is a replace-all, but it is possible to implement a // prompting replace. var from = prompt("Enter search string:", ""), to; if (from) to = prompt("What should it be replaced with?", ""); if (to == null) return; var cursor = this.mirror.getSearchCursor(from, false); while (cursor.findNext()) cursor.replace(to); }, */ jump: function() { var line = prompt("Jump to line:", ""); if (line && !isNaN(Number(line))) { this.mirror.setCursor(Number(line),0); this.mirror.setSelection({line:Number(line),ch:0},{line:Number(line)+1,ch:0}); this.mirror.focus(); } }, /* line: function(){ alert("The cursor is currently at line " + this.mirror.currentLine()); this.mirror.focus(); }, macro: function(){ var name = prompt("Name your constructor:", ""); if (name) this.mirror.replaceSelection("function " + name + "() {\n \n}\n\n" + name + ".prototype = {\n \n};\n"); }, */ reindent: function() { var lineCount = this.mirror.lineCount(); for(var line = 0; line < lineCount; line++) { this.mirror.indentLine(line); } }, about : function() { string = "CodeMirror-UI was written by Jeremy Green (http://www.octolabs.com/) as a light interface around CodeMirror by Marijn Haverbeke (http://codemirror.net)." string += "\n\n" string += "Documentation and the code can be found at https://github.com/jagthedrummer/codemirror-ui/." alert(string); }, reindentSelection: function() { var cur = this.mirror.getCursor() //console.log(cur) var start = this.mirror.getCursor(true)["line"] var end = this.mirror.getCursor(false)["line"] for(var line = start; line <= end; line++) { this.mirror.indentLine(line); } //this.mirror.reindentSelection(); }, // Event handler registration. If disconnect is true, it'll return a // function that unregisters the handler. // Borrowed from CodeMirror + modified connect: function (node, type, handler, disconnect) { /*function wrapHandler(event) { handler(new Event(event || window.event)); }*/ if (typeof node.addEventListener == "function") { node.addEventListener(type, handler, false); if (disconnect) return function() { node.removeEventListener(type, handler, false); }; } else { node.attachEvent("on" + type, handler); if (disconnect) return function() { node.detachEvent("on" + type, handler); }; } } }; /* * This makes coding callbacks much more sane */ Function.prototype.cmuiBind = function(scope) { var _function = this; return function() { return _function.apply(scope, arguments); } }