504 lines
16 KiB
JavaScript
Vendored
504 lines
16 KiB
JavaScript
Vendored
/* 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.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.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);
|
|
}
|
|
}
|