Compare commits

...

11 Commits

Author SHA1 Message Date
Matt Pass
abd61689f1 Better handling of changed files
Insetad of notifying user that file they're attempting to save, now they
can revert
This means the file they're trying to save is reverted to the server
file
Changes they have made are copied into a new file
User can optionally cancel to avoid this
2012-05-26 22:25:12 +01:00
Matt Pass
acbe46b013 Version No & CodeMirror Version No Update 2012-05-26 22:22:45 +01:00
Matt Pass
bbf169907a Update CodeMirror to v.2.25
Update to latest version
2012-05-26 22:22:06 +01:00
Matt Pass
1d3af5c62d Remove CodeMirror 2.24
Update to newer version 2.25
2012-05-26 22:20:40 +01:00
Matt Pass
d095e6a5fe CoffeeScript Support & Update Lang on Tab Switch
CoffeeScript is now supported as a language by ICEcoder
When you switch tabs, the cursor display info also now changes
2012-05-25 17:44:12 +01:00
Matt Pass
96df3ac91b Shortening of hex codes & swapping tab text cols
Hex codes shortened if poss (ie, #888888 becomes #888 and #ff0000
becomes #f00)
Swapped text colour on tabs, selected now has black text, others white
text
2012-05-25 17:03:12 +01:00
Matt Pass
38fe4fd85e Merge branch 'master' of https://github.com/mattpass/ICEcoder 2012-05-25 15:29:20 +01:00
Matt Pass
95ac9b50e0 Republish only 2012-05-25 15:26:43 +01:00
Matt Pass
1f26ddaf3d Another slight format fix 2012-05-24 18:23:48 +01:00
Matt Pass
64231d7f01 Minor format fixes 2012-05-24 18:22:09 +01:00
Matt Pass
656c10e5cf Added MIT license with note about sub-licenses 2012-05-24 18:18:03 +01:00
62 changed files with 2077 additions and 625 deletions

View File

@@ -1,230 +0,0 @@
(function() {
var count = "";
var sdir = "f";
var buf = "";
var yank = 0;
var mark = [];
function emptyBuffer() { buf = ""; }
function pushInBuffer(str) { buf += str; };
function pushCountDigit(digit) { return function(cm) {count += digit;} }
function popCount() { var i = parseInt(count); count = ""; return i || 1; }
function countTimes(func) {
if (typeof func == "string") func = CodeMirror.commands[func];
return function(cm) { for (var i = 0, c = popCount(); i < c; ++i) func(cm); }
}
function iterObj(o, f) {
for (var prop in o) if (o.hasOwnProperty(prop)) f(prop, o[prop]);
}
var word = [/\w/, /[^\w\s]/], bigWord = [/\S/];
function findWord(line, pos, dir, regexps) {
var stop = 0, next = -1;
if (dir > 0) { stop = line.length; next = 0; }
var start = stop, end = stop;
// Find bounds of next one.
outer: for (; pos != stop; pos += dir) {
for (var i = 0; i < regexps.length; ++i) {
if (regexps[i].test(line.charAt(pos + next))) {
start = pos;
for (; pos != stop; pos += dir) {
if (!regexps[i].test(line.charAt(pos + next))) break;
}
end = pos;
break outer;
}
}
}
return {from: Math.min(start, end), to: Math.max(start, end)};
}
function moveToWord(cm, regexps, dir, where) {
var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line), word;
while (true) {
word = findWord(line, ch, dir, regexps);
ch = word[where == "end" ? "to" : "from"];
if (ch == cur.ch && word.from != word.to) ch = word[dir < 0 ? "from" : "to"];
else break;
}
cm.setCursor(cur.line, word[where == "end" ? "to" : "from"], true);
}
function joinLineNext(cm) {
var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line);
CodeMirror.commands.goLineEnd(cm);
if (cur.line != cm.lineCount()) {
CodeMirror.commands.goLineEnd(cm);
cm.replaceSelection(" ", "end");
CodeMirror.commands.delCharRight(cm);
}
}
function editCursor(mode) {
if (mode == "vim-insert") {
// put in your cursor css changing code
} else if (mode == "vim") {
// put in your cursor css changing code
}
}
function delTillMark(cm, cHar) {
var i = mark[cHar], l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
cm.setCursor(start);
for (var c = start; c <= end; c++) {
pushInBuffer("\n"+cm.getLine(start));
cm.removeLine(start);
}
}
function yankTillMark(cm, cHar) {
var i = mark[cHar], l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
for (var c = start; c <= end; c++) {
pushInBuffer("\n"+cm.getLine(c));
}
cm.setCursor(start);
}
function goLineStartText(cm) {
// Go to the start of the line where the text begins, or the end for whitespace-only lines
var cur = cm.getCursor(), firstNonWS = cm.getLine(cur.line).search(/\S/);
cm.setCursor(cur.line, firstNonWS == -1 ? line.length : firstNonWS, true);
}
var map = CodeMirror.keyMap.vim = {
"0": function(cm) {count.length > 0 ? pushCountDigit("0")(cm) : CodeMirror.commands.goLineStart(cm);},
// Pipe (|); TODO: should be *screen* chars, so need a util function to turn tabs into spaces?
"'|'": function(cm) {cm.setCursor(cm.getCursor().line, popCount() - 1, true);},
"'^'": function(cm) {popCount(); goLineStartText(cm);},
"A": function(cm) {popCount(); cm.setCursor(cm.getCursor().line, cm.getCursor().ch+1, true); cm.setOption("keyMap", "vim-insert"); editCursor("vim-insert");},
"Shift-A": function(cm) {popCount(); CodeMirror.commands.goLineEnd(cm); cm.setOption("keyMap", "vim-insert"); editCursor("vim-insert");},
"I": function(cm) {popCount(); cm.setOption("keyMap", "vim-insert"); editCursor("vim-insert");},
"Shift-I": function(cm) {popCount(); goLineStartText(cm); cm.setOption("keyMap", "vim-insert"); editCursor("vim-insert");},
"O": function(cm) {popCount(); CodeMirror.commands.goLineEnd(cm); CodeMirror.commands.newlineAndIndent(cm); cm.setOption("keyMap", "vim-insert"); editCursor("vim-insert");},
"Shift-O": function(cm) {popCount(); CodeMirror.commands.goLineStart(cm); cm.replaceSelection("\n", "start"); cm.indentLine(cm.getCursor().line); cm.setOption("keyMap", "vim-insert"); editCursor("vim-insert");},
"G": function(cm) {cm.setOption("keyMap", "vim-prefix-g");},
"D": function(cm) {cm.setOption("keyMap", "vim-prefix-d"); emptyBuffer();},
"Shift-D": function(cm) {
emptyBuffer();
mark["Shift-D"] = cm.getCursor(false).line;
cm.setCursor(cm.getCursor(true).line);
delTillMark(cm,"Shift-D"); mark = [];
},
"M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = [];},
"Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer(); yank = 0;},
"Shift-Y": function(cm) {
emptyBuffer();
mark["Shift-D"] = cm.getCursor(false).line;
cm.setCursor(cm.getCursor(true).line);
yankTillMark(cm,"Shift-D"); mark = [];
},
"/": function(cm) {var f = CodeMirror.commands.find; f && f(cm); sdir = "f"},
"'?'": function(cm) {
var f = CodeMirror.commands.find;
if (f) { f(cm); CodeMirror.commands.findPrev(cm); sdir = "r"; }
},
"N": function(cm) {
var fn = CodeMirror.commands.findNext;
if (fn) sdir != "r" ? fn(cm) : CodeMirror.commands.findPrev(cm);
},
"Shift-N": function(cm) {
var fn = CodeMirror.commands.findNext;
if (fn) sdir != "r" ? CodeMirror.commands.findPrev(cm) : fn.findNext(cm);
},
"Shift-G": function(cm) {count == "" ? cm.setCursor(cm.lineCount()) : cm.setCursor(parseInt(count)-1); popCount(); CodeMirror.commands.goLineStart(cm);},
nofallthrough: true
};
// Add bindings for number keys
for (var i = 1; i < 10; ++i) map[i] = pushCountDigit(i);
// Add bindings that are influenced by number keys
iterObj({
"H": "goColumnLeft", "L": "goColumnRight", "J": "goLineDown",
"K": "goLineUp", "Left": "goColumnLeft", "Right": "goColumnRight",
"Down": "goLineDown", "Up": "goLineUp", "Backspace": "goCharLeft",
"Space": "goCharRight",
"B": function(cm) {moveToWord(cm, word, -1, "end");},
"E": function(cm) {moveToWord(cm, word, 1, "end");},
"W": function(cm) {moveToWord(cm, word, 1, "start");},
"Shift-B": function(cm) {moveToWord(cm, bigWord, -1, "end");},
"Shift-E": function(cm) {moveToWord(cm, bigWord, 1, "end");},
"Shift-W": function(cm) {moveToWord(cm, bigWord, 1, "start");},
"X": function(cm) {CodeMirror.commands.delCharRight(cm)},
"P": function(cm) {
var cur = cm.getCursor().line;
if (buf!= "") {
CodeMirror.commands.goLineEnd(cm);
cm.replaceSelection(buf, "end");
}
cm.setCursor(cur+1);
},
"Shift-X": function(cm) {CodeMirror.commands.delCharLeft(cm)},
"Shift-J": function(cm) {joinLineNext(cm)},
"'~'": function(cm) {
var cur = cm.getCursor(), cHar = cm.getRange({line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1});
cHar = cHar != cHar.toLowerCase() ? cHar.toLowerCase() : cHar.toUpperCase();
cm.replaceRange(cHar, {line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1});
cm.setCursor(cur.line, cur.ch+1);
},
"Ctrl-B": function(cm) {CodeMirror.commands.goPageUp(cm)},
"Ctrl-F": function(cm) {CodeMirror.commands.goPageDown(cm)},
"Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
"U": "undo", "Ctrl-R": "redo", "'$'": "goLineEnd"
}, function(key, cmd) { map[key] = countTimes(cmd); });
CodeMirror.keyMap["vim-prefix-g"] = {
"E": countTimes(function(cm) { moveToWord(cm, word, -1, "start");}),
"Shift-E": countTimes(function(cm) { moveToWord(cm, bigWord, -1, "start");}),
auto: "vim",
nofallthrough: true
};
CodeMirror.keyMap["vim-prefix-m"] = {
auto: "vim",
nofallthrough: true
};
CodeMirror.keyMap["vim-prefix-d"] = {
"D": countTimes(function(cm) { pushInBuffer("\n"+cm.getLine(cm.getCursor().line)); cm.removeLine(cm.getCursor().line); }),
"'": function(cm) {cm.setOption("keyMap", "vim-prefix-d'"); emptyBuffer();},
auto: "vim",
nofallthrough: true
};
CodeMirror.keyMap["vim-prefix-d'"] = {
auto: "vim",
nofallthrough: true
};
CodeMirror.keyMap["vim-prefix-y'"] = {
auto: "vim",
nofallthrough: true
};
// iterate through uppercase alphabet char codes
for (var i = 65; i < 65 + 26; i++) {
// apply for `letter` and 'Shift-' + `letter`
for (var m = String.fromCharCode(i); m.length < 8; m = "Shift-" + m) {
CodeMirror.keyMap["vim-prefix-m"][m] = function(cm) {
mark[m] = cm.getCursor().line;
};
CodeMirror.keyMap["vim-prefix-d'"][m] = function(cm) {
delTillMark(cm,m);
};
CodeMirror.keyMap["vim-prefix-y'"][m] = function(cm) {
yankTillMark(cm,m);
};
}
}
CodeMirror.keyMap["vim-prefix-y"] = {
"Y": countTimes(function(cm) { pushInBuffer("\n"+cm.getLine(cm.getCursor().line+yank)); yank++; }),
"'": function(cm) {cm.setOption("keyMap", "vim-prefix-y'"); emptyBuffer();},
auto: "vim",
nofallthrough: true
};
CodeMirror.keyMap["vim-insert"] = {
// TODO: override navigation keys so that Esc will cancel automatic indentation from o, O, i_<CR>
"Esc": function(cm) {
cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
cm.setOption("keyMap", "vim");
editCursor("vim");
},
"Ctrl-N": function(cm) {/* Code to bring up autocomplete hint */},
"Ctrl-P": function(cm) {/* Code to bring up autocomplete hint */},
fallthrough: ["default"]
};
})();

View File

@@ -18,7 +18,7 @@
"Alt-Y": function(cm) {cm.replaceSelection(popFromRing());},
"Ctrl-/": "undo", "Shift-Ctrl--": "undo", "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace",
"Ctrl-Z": "undo", "Cmd-Z": "undo",
"Ctrl-Z": "undo", "Cmd-Z": "undo", "Alt-/": "autocomplete",
fallthrough: ["basic", "emacsy"]
};

View File

@@ -0,0 +1,500 @@
// Supported keybindings:
//
// Cursor movement:
// h, j, k, l
// e, E, w, W, b, B
// Ctrl-f, Ctrl-b
// Ctrl-n, Ctrl-p
// $, ^, 0
// G
// ge, gE
// gg
// f<char>, F<char>, t<char>, T<char>
// Ctrl-o, Ctrl-i TODO (FIXME - Ctrl-O wont work in Chrome)
// /, ?, n, N TODO (does not work)
// #, * TODO
//
// Entering insert mode:
// i, I, a, A, o, O
// s
// ce, cb (without support for number of actions like c3e - TODO)
// cc
// S, C TODO
// cf<char>, cF<char>, ct<char>, cT<char>
//
// Deleting text:
// x, X
// J
// dd, D
// de, db (without support for number of actions like d3e - TODO)
// df<char>, dF<char>, dt<char>, dT<char>
//
// Yanking and pasting:
// yy, Y
// p, P
// p'<char> TODO - test
// y'<char> TODO - test
// m<char> TODO - test
//
// Changing text in place:
// ~
// r<char>
//
// Visual mode:
// v, V TODO
//
// Misc:
// . TODO
//
(function() {
var count = "";
var sdir = "f";
var buf = "";
var yank = 0;
var mark = [];
function emptyBuffer() { buf = ""; }
function pushInBuffer(str) { buf += str; };
function pushCountDigit(digit) { return function(cm) {count += digit;} }
function popCount() { var i = parseInt(count); count = ""; return i || 1; }
function iterTimes(func) {
for (var i = 0, c = popCount(); i < c; ++i) func(i, i == c - 1);
}
function countTimes(func) {
if (typeof func == "string") func = CodeMirror.commands[func];
return function(cm) { iterTimes(function () { func(cm); }) };
}
function iterObj(o, f) {
for (var prop in o) if (o.hasOwnProperty(prop)) f(prop, o[prop]);
}
function iterList(l, f) {
for (var i in l) f(l[i]);
}
function toLetter(ch) {
// T -> t, Shift-T -> T, '*' -> *, "Space" -> " "
if (ch.slice(0, 6) == "Shift-") {
return ch.slice(0, 1);
} else {
if (ch == "Space") return " ";
if (ch.length == 3 && ch[0] == "'" && ch[2] == "'") return ch[1];
return ch.toLowerCase();
}
}
var SPECIAL_SYMBOLS = "~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'1234567890";
function toCombo(ch) {
// t -> T, T -> Shift-T, * -> '*', " " -> "Space"
if (ch == " ") return "Space";
var specialIdx = SPECIAL_SYMBOLS.indexOf(ch);
if (specialIdx != -1) return "'" + ch + "'";
if (ch.toLowerCase() == ch) return ch.toUpperCase();
return "Shift-" + ch.toUpperCase();
}
var word = [/\w/, /[^\w\s]/], bigWord = [/\S/];
function findWord(line, pos, dir, regexps) {
var stop = 0, next = -1;
if (dir > 0) { stop = line.length; next = 0; }
var start = stop, end = stop;
// Find bounds of next one.
outer: for (; pos != stop; pos += dir) {
for (var i = 0; i < regexps.length; ++i) {
if (regexps[i].test(line.charAt(pos + next))) {
start = pos;
for (; pos != stop; pos += dir) {
if (!regexps[i].test(line.charAt(pos + next))) break;
}
end = pos;
break outer;
}
}
}
return {from: Math.min(start, end), to: Math.max(start, end)};
}
function moveToWord(cm, regexps, dir, where) {
var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line), word;
while (true) {
word = findWord(line, ch, dir, regexps);
ch = word[where == "end" ? "to" : "from"];
if (ch == cur.ch && word.from != word.to) ch = word[dir < 0 ? "from" : "to"];
else break;
}
cm.setCursor(cur.line, word[where == "end" ? "to" : "from"], true);
}
function joinLineNext(cm) {
var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line);
CodeMirror.commands.goLineEnd(cm);
if (cur.line != cm.lineCount()) {
CodeMirror.commands.goLineEnd(cm);
cm.replaceSelection(" ", "end");
CodeMirror.commands.delCharRight(cm);
}
}
function delTillMark(cm, cHar) {
var i = mark[cHar];
if (i === undefined) {
// console.log("Mark not set"); // TODO - show in status bar
return;
}
var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
cm.setCursor(start);
for (var c = start; c <= end; c++) {
pushInBuffer("\n"+cm.getLine(start));
cm.removeLine(start);
}
}
function yankTillMark(cm, cHar) {
var i = mark[cHar];
if (i === undefined) {
// console.log("Mark not set"); // TODO - show in status bar
return;
}
var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
for (var c = start; c <= end; c++) {
pushInBuffer("\n"+cm.getLine(c));
}
cm.setCursor(start);
}
function goLineStartText(cm) {
// Go to the start of the line where the text begins, or the end for whitespace-only lines
var cur = cm.getCursor(), firstNonWS = cm.getLine(cur.line).search(/\S/);
cm.setCursor(cur.line, firstNonWS == -1 ? line.length : firstNonWS, true);
}
function charIdxInLine(cm, cHar, motion_options) {
// Search for cHar in line.
// motion_options: {forward, inclusive}
// If inclusive = true, include it too.
// If forward = true, search forward, else search backwards.
// If char is not found on this line, do nothing
var cur = cm.getCursor(), line = cm.getLine(cur.line), idx;
var ch = toLetter(cHar), mo = motion_options;
if (mo.forward) {
idx = line.indexOf(ch, cur.ch + 1);
if (idx != -1 && mo.inclusive) idx += 1;
} else {
idx = line.lastIndexOf(ch, cur.ch);
if (idx != -1 && !mo.inclusive) idx += 1;
}
return idx;
}
function moveTillChar(cm, cHar, motion_options) {
// Move to cHar in line, as found by charIdxInLine.
var idx = charIdxInLine(cm, cHar, motion_options), cur = cm.getCursor();
if (idx != -1) cm.setCursor({line: cur.line, ch: idx});
}
function delTillChar(cm, cHar, motion_options) {
// delete text in this line, untill cHar is met,
// as found by charIdxInLine.
// If char is not found on this line, do nothing
var idx = charIdxInLine(cm, cHar, motion_options);
var cur = cm.getCursor();
if (idx !== -1) {
if (motion_options.forward) {
cm.replaceRange("", {line: cur.line, ch: cur.ch}, {line: cur.line, ch: idx});
} else {
cm.replaceRange("", {line: cur.line, ch: idx}, {line: cur.line, ch: cur.ch});
}
}
}
function enterInsertMode(cm) {
// enter insert mode: switch mode and cursor
if (!cm) console.log("call enterInsertMode with 'cm' as an argument");
popCount();
cm.setOption("keyMap", "vim-insert");
}
// main keymap
var map = CodeMirror.keyMap.vim = {
// Pipe (|); TODO: should be *screen* chars, so need a util function to turn tabs into spaces?
"'|'": function(cm) {
cm.setCursor(cm.getCursor().line, popCount() - 1, true);
},
"'^'": function(cm) { popCount(); goLineStartText(cm);},
"A": function(cm) {
cm.setCursor(cm.getCursor().line, cm.getCursor().ch+1, true);
enterInsertMode(cm);
},
"Shift-A": function(cm) { CodeMirror.commands.goLineEnd(cm); enterInsertMode(cm);},
"I": function(cm) { enterInsertMode(cm);},
"Shift-I": function(cm) { goLineStartText(cm); enterInsertMode(cm);},
"O": function(cm) {
CodeMirror.commands.goLineEnd(cm);
CodeMirror.commands.newlineAndIndent(cm);
enterInsertMode(cm);
},
"Shift-O": function(cm) {
CodeMirror.commands.goLineStart(cm);
cm.replaceSelection("\n", "start");
cm.indentLine(cm.getCursor().line);
enterInsertMode(cm);
},
"G": function(cm) { cm.setOption("keyMap", "vim-prefix-g");},
"Shift-D": function(cm) {
// commented out verions works, but I left original, cause maybe
// I don't know vim enouth to see what it does
/* var cur = cm.getCursor();
var f = {line: cur.line, ch: cur.ch}, t = {line: cur.line};
pushInBuffer(cm.getRange(f, t));
cm.replaceRange("", f, t);
*/
emptyBuffer();
mark["Shift-D"] = cm.getCursor(false).line;
cm.setCursor(cm.getCursor(true).line);
delTillMark(cm,"Shift-D"); mark = [];
},
"S": function (cm) {
countTimes(function (_cm) {
CodeMirror.commands.delCharRight(_cm);
})(cm);
enterInsertMode(cm);
},
"M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = [];},
"Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer(); yank = 0;},
"Shift-Y": function(cm) {
emptyBuffer();
mark["Shift-D"] = cm.getCursor(false).line;
cm.setCursor(cm.getCursor(true).line);
yankTillMark(cm,"Shift-D"); mark = [];
},
"/": function(cm) {var f = CodeMirror.commands.find; f && f(cm); sdir = "f";},
"'?'": function(cm) {
var f = CodeMirror.commands.find;
if (f) { f(cm); CodeMirror.commands.findPrev(cm); sdir = "r"; }
},
"N": function(cm) {
var fn = CodeMirror.commands.findNext;
if (fn) sdir != "r" ? fn(cm) : CodeMirror.commands.findPrev(cm);
},
"Shift-N": function(cm) {
var fn = CodeMirror.commands.findNext;
if (fn) sdir != "r" ? CodeMirror.commands.findPrev(cm) : fn.findNext(cm);
},
"Shift-G": function(cm) {
count == "" ? cm.setCursor(cm.lineCount()) : cm.setCursor(parseInt(count)-1);
popCount();
CodeMirror.commands.goLineStart(cm);
},
"'$'": function (cm) {
countTimes("goLineEnd")(cm);
if (cm.getCursor().ch) CodeMirror.commands.goColumnLeft(cm);
},
nofallthrough: true, style: "fat-cursor"
};
// standard mode switching
iterList(["d", "t", "T", "f", "F", "c", "r"],
function (ch) {
CodeMirror.keyMap.vim[toCombo(ch)] = function (cm) {
cm.setOption("keyMap", "vim-prefix-" + ch);
emptyBuffer();
};
});
function addCountBindings(keyMap) {
// Add bindings for number keys
keyMap["0"] = function(cm) {
count.length > 0 ? pushCountDigit("0")(cm) : CodeMirror.commands.goLineStart(cm);
};
for (var i = 1; i < 10; ++i) keyMap[i] = pushCountDigit(i);
}
addCountBindings(CodeMirror.keyMap.vim);
// main num keymap
// Add bindings that are influenced by number keys
iterObj({
"H": "goColumnLeft", "L": "goColumnRight", "J": "goLineDown",
"K": "goLineUp", "Left": "goColumnLeft", "Right": "goColumnRight",
"Down": "goLineDown", "Up": "goLineUp", "Backspace": "goCharLeft",
"Space": "goCharRight",
"B": function(cm) {moveToWord(cm, word, -1, "end");},
"E": function(cm) {moveToWord(cm, word, 1, "end");},
"W": function(cm) {moveToWord(cm, word, 1, "start");},
"Shift-B": function(cm) {moveToWord(cm, bigWord, -1, "end");},
"Shift-E": function(cm) {moveToWord(cm, bigWord, 1, "end");},
"Shift-W": function(cm) {moveToWord(cm, bigWord, 1, "start");},
"X": function(cm) {CodeMirror.commands.delCharRight(cm);},
"P": function(cm) {
var cur = cm.getCursor().line;
if (buf!= "") {
CodeMirror.commands.goLineEnd(cm);
cm.replaceSelection(buf, "end");
}
cm.setCursor(cur+1);
},
"Shift-X": function(cm) {CodeMirror.commands.delCharLeft(cm);},
"Shift-J": function(cm) {joinLineNext(cm);},
"Shift-P": function(cm) {
var cur = cm.getCursor().line;
if (buf!= "") {
CodeMirror.commands.goLineUp(cm);
CodeMirror.commands.goLineEnd(cm);
cm.replaceSelection(buf, "end");
}
cm.setCursor(cur+1);
},
"'~'": function(cm) {
var cur = cm.getCursor(), cHar = cm.getRange({line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1});
cHar = cHar != cHar.toLowerCase() ? cHar.toLowerCase() : cHar.toUpperCase();
cm.replaceRange(cHar, {line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1});
cm.setCursor(cur.line, cur.ch+1);
},
"Ctrl-B": function(cm) {CodeMirror.commands.goPageUp(cm);},
"Ctrl-F": function(cm) {CodeMirror.commands.goPageDown(cm);},
"Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
"U": "undo", "Ctrl-R": "redo"
}, function(key, cmd) { map[key] = countTimes(cmd); });
// empty key maps
iterList([
"vim-prefix-d'",
"vim-prefix-y'",
"vim-prefix-df",
"vim-prefix-dF",
"vim-prefix-dt",
"vim-prefix-dT",
"vim-prefix-c",
"vim-prefix-cf",
"vim-prefix-cF",
"vim-prefix-ct",
"vim-prefix-cT",
"vim-prefix-",
"vim-prefix-f",
"vim-prefix-F",
"vim-prefix-t",
"vim-prefix-T",
"vim-prefix-r",
"vim-prefix-m"
],
function (prefix) {
CodeMirror.keyMap[prefix] = {
auto: "vim",
nofallthrough: true
};
});
CodeMirror.keyMap["vim-prefix-g"] = {
"E": countTimes(function(cm) { moveToWord(cm, word, -1, "start");}),
"Shift-E": countTimes(function(cm) { moveToWord(cm, bigWord, -1, "start");}),
"G": function (cm) { cm.setCursor({line: 0, ch: cm.getCursor().ch});},
auto: "vim", nofallthrough: true, style: "fat-cursor"
};
CodeMirror.keyMap["vim-prefix-d"] = {
"D": countTimes(function(cm) {
pushInBuffer("\n"+cm.getLine(cm.getCursor().line));
cm.removeLine(cm.getCursor().line);
}),
"'": function(cm) {
cm.setOption("keyMap", "vim-prefix-d'");
emptyBuffer();
},
"E": countTimes("delWordRight"),
"B": countTimes("delWordLeft"),
auto: "vim", nofallthrough: true, style: "fat-cursor"
};
// FIXME - does not work for bindings like "d3e"
addCountBindings(CodeMirror.keyMap["vim-prefix-d"]);
CodeMirror.keyMap["vim-prefix-c"] = {
"E": function (cm) {
countTimes("delWordRight")(cm);
enterInsertMode(cm);
},
"B": function (cm) {
countTimes("delWordLeft")(cm);
enterInsertMode(cm);
},
"C": function (cm) {
iterTimes(function (i, last) {
CodeMirror.commands.deleteLine(cm);
if (i) {
CodeMirror.commands.delCharRight(cm);
if (last) CodeMirror.commands.deleteLine(cm);
}
});
enterInsertMode(cm);
},
auto: "vim", nofallthrough: true, style: "fat-cursor"
};
iterList(["vim-prefix-d", "vim-prefix-c", "vim-prefix-"], function (prefix) {
iterList(["f", "F", "T", "t"],
function (ch) {
CodeMirror.keyMap[prefix][toCombo(ch)] = function (cm) {
cm.setOption("keyMap", prefix + ch);
emptyBuffer();
};
});
});
var MOTION_OPTIONS = {
"t": {inclusive: false, forward: true},
"f": {inclusive: true, forward: true},
"T": {inclusive: false, forward: false},
"F": {inclusive: true, forward: false}
};
function setupPrefixBindingForKey(m) {
CodeMirror.keyMap["vim-prefix-m"][m] = function(cm) {
mark[m] = cm.getCursor().line;
};
CodeMirror.keyMap["vim-prefix-d'"][m] = function(cm) {
delTillMark(cm,m);
};
CodeMirror.keyMap["vim-prefix-y'"][m] = function(cm) {
yankTillMark(cm,m);
};
CodeMirror.keyMap["vim-prefix-r"][m] = function (cm) {
var cur = cm.getCursor();
cm.replaceRange(toLetter(m),
{line: cur.line, ch: cur.ch},
{line: cur.line, ch: cur.ch + 1});
CodeMirror.commands.goColumnLeft(cm);
};
// all commands, related to motions till char in line
iterObj(MOTION_OPTIONS, function (ch, options) {
CodeMirror.keyMap["vim-prefix-" + ch][m] = function(cm) {
moveTillChar(cm, m, options);
};
CodeMirror.keyMap["vim-prefix-d" + ch][m] = function(cm) {
delTillChar(cm, m, options);
};
CodeMirror.keyMap["vim-prefix-c" + ch][m] = function(cm) {
delTillChar(cm, m, options);
enterInsertMode(cm);
};
});
};
for (var i = 65; i < 65 + 26; i++) { // uppercase alphabet char codes
var ch = String.fromCharCode(i);
setupPrefixBindingForKey(toCombo(ch));
setupPrefixBindingForKey(toCombo(ch.toLowerCase()));
}
iterList(SPECIAL_SYMBOLS, function (ch) {
setupPrefixBindingForKey(toCombo(ch));
});
setupPrefixBindingForKey("Space");
CodeMirror.keyMap["vim-prefix-y"] = {
"Y": countTimes(function(cm) { pushInBuffer("\n"+cm.getLine(cm.getCursor().line+yank)); yank++; }),
"'": function(cm) {cm.setOption("keyMap", "vim-prefix-y'"); emptyBuffer();},
auto: "vim", nofallthrough: true, style: "fat-cursor"
};
CodeMirror.keyMap["vim-insert"] = {
// TODO: override navigation keys so that Esc will cancel automatic indentation from o, O, i_<CR>
"Esc": function(cm) {
cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
cm.setOption("keyMap", "vim");
},
"Ctrl-N": "autocomplete",
"Ctrl-P": "autocomplete",
fallthrough: ["default"]
};
})();

View File

@@ -43,6 +43,7 @@
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
}
.CodeMirror-wrap pre {
@@ -63,8 +64,19 @@
position: absolute;
visibility: hidden;
border-left: 1px solid black;
border-right:none;
width:0;
border-right: none;
width: 0;
}
.cm-keymap-fat-cursor pre.CodeMirror-cursor {
width: auto;
border: 0;
background: transparent;
background: rgba(0, 200, 0, .4);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
}
/* Kludge to turn off filter in ie9+, which also accepts rgba */
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
.CodeMirror-focused pre.CodeMirror-cursor {
@@ -100,7 +112,7 @@ div.CodeMirror-selected { background: #d9d9d9; }
.cm-s-default span.cm-bracket {color: #cc7;}
.cm-s-default span.cm-tag {color: #170;}
.cm-s-default span.cm-attribute {color: #00c;}
.cm-s-default span.cm-header {color: #a0a;}
.cm-s-default span.cm-header {color: blue;}
.cm-s-default span.cm-quote {color: #090;}
.cm-s-default span.cm-hr {color: #999;}
.cm-s-default span.cm-link {color: #00c;}

View File

@@ -1,3 +1,5 @@
// CodeMirror version 2.25
//
// All functions that need access to the editor's state live inside
// the CodeMirror function. Below that, at the bottom of the file,
// some utilities are defined.
@@ -78,7 +80,7 @@ var CodeMirror = (function() {
// Variables used by startOperation/endOperation to track what
// happened during the operation.
var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone,
gutterDirty, callbacks;
gutterDirty, callbacks, maxLengthChanged;
// Current visible range (may be bigger than the view window).
var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0;
// bracketHighlighted is used to remember that a bracket has been
@@ -370,6 +372,7 @@ var CodeMirror = (function() {
return;
case 2:
if (start) setCursor(start.line, start.ch, true);
setTimeout(focusInput, 20);
return;
}
// For button 1, if it was clicked inside the editor
@@ -593,7 +596,7 @@ var CodeMirror = (function() {
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
if (((window.opera && !e.which) || khtml) && handleKeyBinding(e)) return;
if (((window.opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return;
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
if (mode.electricChars.indexOf(ch) > -1)
@@ -668,7 +671,7 @@ var CodeMirror = (function() {
var recomputeMaxLength = false, maxLineLength = maxLine.length;
if (!options.lineWrapping)
doc.iter(from.line, to.line + 1, function(line) {
if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
});
if (from.line != to.line || newText.length > 1) gutterDirty = true;
@@ -724,20 +727,12 @@ var CodeMirror = (function() {
} else {
doc.iter(from.line, from.line + newText.length, function(line) {
var l = line.text;
if (l.length > maxLineLength) {
if (!line.hidden && l.length > maxLineLength) {
maxLine = l; maxLineLength = l.length; maxWidth = null;
recomputeMaxLength = false;
}
});
if (recomputeMaxLength) {
maxLineLength = 0; maxLine = ""; maxWidth = null;
doc.iter(0, doc.size, function(line) {
var l = line.text;
if (l.length > maxLineLength) {
maxLineLength = l.length; maxLine = l;
}
});
}
if (recomputeMaxLength) maxLengthChanged = true;
}
// Add these lines to the work array, so that they will be
@@ -769,6 +764,18 @@ var CodeMirror = (function() {
if (scroller.clientHeight)
code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
}
function computeMaxLength() {
var maxLineLength = 0;
maxLine = ""; maxWidth = null;
doc.iter(0, doc.size, function(line) {
var l = line.text;
if (!line.hidden && l.length > maxLineLength) {
maxLineLength = l.length; maxLine = l;
}
});
maxLengthChanged = false;
}
function replaceRange(code, from, to) {
from = clipPos(from);
@@ -856,7 +863,8 @@ var CodeMirror = (function() {
else if (overwrite && posEq(sel.from, sel.to))
sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
replaceSelection(text.slice(same), "end");
prevInput = text;
if (text.length > 1000) { input.value = prevInput = ""; }
else prevInput = text;
return true;
}
function resetInput(user) {
@@ -1493,6 +1501,16 @@ var CodeMirror = (function() {
return changeLine(handle, function(line, no) {
if (line.hidden != hidden) {
line.hidden = hidden;
if (!options.lineWrapping) {
var l = line.text;
if (hidden && l.length == maxLine.length) {
maxLengthChanged = true;
}
else if (!hidden && l.length > maxLine.length) {
maxLine = l; maxWidth = null;
maxLengthChanged = false;
}
}
updateLineHeight(line, hidden ? 0 : 1);
var fline = sel.from.line, tline = sel.to.line;
if (hidden && (fline == no || tline == no)) {
@@ -1845,6 +1863,7 @@ var CodeMirror = (function() {
}
function endOperation() {
var reScroll = false, updated;
if (maxLengthChanged) computeMaxLength();
if (selectionChanged) reScroll = !scrollCursorIntoView();
if (changes.length) updated = updateDisplay(changes, true);
else {
@@ -2484,8 +2503,10 @@ var CodeMirror = (function() {
if (wrapWBR) html.push("<wbr>");
}
html.push(open);
span_(text.slice(wrapAt - outPos), style);
var cut = wrapAt - outPos;
span_(window.opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
html.push("</span>");
if (window.opera) span_(text.slice(cut + 1), style);
wrapAt--;
outPos += l;
} else {
@@ -2601,11 +2622,7 @@ var CodeMirror = (function() {
},
insertHeight: function(at, lines, height) {
this.height += height;
// The trick below is apparently too advanced for IE, which
// occasionally corrupts this.lines (duplicating elements) when
// it is used.
if (ie) this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
else this.lines.splice.apply(this.lines, [at, 0].concat(lines));
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
},
iterN: function(at, n, op) {
@@ -3056,4 +3073,4 @@ var CodeMirror = (function() {
})();
return CodeMirror;
})();
})();

View File

@@ -10,7 +10,6 @@
* following CodeMirror modes and will ignore all others:
* - htmlmixed
* - xml
* - xmlpure
*
* See demos/closetag.html for a usage example.
*
@@ -104,33 +103,6 @@
}
}
} else if (mode == 'xmlpure') {
var pos = cm.getCursor();
var tok = cm.getTokenAt(pos);
var tagName = tok.state.context.tagName;
if (ch == '>') {
// <foo> tagName=foo, string=foo
// <foo /> tagName=foo, string=/ # ignore
// <foo></foo> tagName=foo, string=/foo # ignore
if (tok.string == tagName) {
cm.replaceSelection('>'); // parity w/html modes
pos = {line: pos.line, ch: pos.ch + 1};
cm.setCursor(pos);
insertEndTag(cm, indent, pos, tagName);
return;
}
} else if (ch == '/') {
// <foo / tagName=foo, string= # ignore
// <foo></ tagName=foo, string=<
if (tok.string == '<') {
completeEndTag(cm, pos, tagName);
return;
}
}
}
throw CodeMirror.Pass; // Bubble if not handled

View File

@@ -21,7 +21,8 @@
}
CodeMirror.requireMode = function(mode, cont) {
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont());
if (typeof mode != "string") mode = mode.name;
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
var script = document.createElement("script");

View File

@@ -27,7 +27,7 @@
return isRE ? new RegExp(isRE[1]) : query;
}
var queryDialog =
'Search: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
function doSearch(cm, rev) {
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
@@ -63,8 +63,8 @@
})}
var replaceQueryDialog =
'Replace: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
var replacementQueryDialog = 'With: <input type="text" style="width: 10em">';
'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
function replace(cm, all) {
dialog(cm, replaceQueryDialog, "Replace:", function(query) {

View File

@@ -1,7 +1,7 @@
(function(){
function SearchCursor(cm, query, pos, caseFold) {
this.atOccurrence = false; this.cm = cm;
if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
if (caseFold == null && typeof query == "string") caseFold = false;
pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
this.pos = {from: pos, to: pos};

View File

@@ -0,0 +1,22 @@
The MIT License
Copyright (c) 2011 Jeff Pickhardt
Modified from the Python CodeMirror mode, Copyright (c) 2010 Timothy Farrell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,347 @@
/**
* Link to the project's GitHub page:
* https://github.com/pickhardt/coffeescript-codemirror-mode
*/
CodeMirror.defineMode('coffeescript', function(conf) {
var ERRORCLASS = 'error';
function wordRegexp(words) {
return new RegExp("^((" + words.join(")|(") + "))\\b");
}
var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]");
var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))");
var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))");
var identifiers = new RegExp("^[_A-Za-z$][_A-Za-z$0-9]*");
var wordOperators = wordRegexp(['and', 'or', 'not',
'is', 'isnt', 'in',
'instanceof', 'typeof']);
var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else',
'switch', 'try', 'catch', 'finally', 'class'];
var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete',
'do', 'in', 'of', 'new', 'return', 'then',
'this', 'throw', 'when', 'until'];
var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
indentKeywords = wordRegexp(indentKeywords);
var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])");
var regexPrefixes = new RegExp("^(/{3}|/)");
var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no'];
var constants = wordRegexp(commonConstants);
// Tokenizers
function tokenBase(stream, state) {
// Handle scope changes
if (stream.sol()) {
var scopeOffset = state.scopes[0].offset;
if (stream.eatSpace()) {
var lineOffset = stream.indentation();
if (lineOffset > scopeOffset) {
return 'indent';
} else if (lineOffset < scopeOffset) {
return 'dedent';
}
return null;
} else {
if (scopeOffset > 0) {
dedent(stream, state);
}
}
}
if (stream.eatSpace()) {
return null;
}
var ch = stream.peek();
// Handle docco title comment (single line)
if (stream.match("####")) {
stream.skipToEnd();
return 'comment';
}
// Handle multi line comments
if (stream.match("###")) {
state.tokenize = longComment;
return state.tokenize(stream, state);
}
// Single line comment
if (ch === '#') {
stream.skipToEnd();
return 'comment';
}
// Handle number literals
if (stream.match(/^-?[0-9\.]/, false)) {
var floatLiteral = false;
// Floats
if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
floatLiteral = true;
}
if (stream.match(/^-?\d+\.\d*/)) {
floatLiteral = true;
}
if (stream.match(/^-?\.\d+/)) {
floatLiteral = true;
}
if (floatLiteral) {
// prevent from getting extra . on 1..
if (stream.peek() == "."){
stream.backUp(1);
}
return 'number';
}
// Integers
var intLiteral = false;
// Hex
if (stream.match(/^-?0x[0-9a-f]+/i)) {
intLiteral = true;
}
// Decimal
if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
intLiteral = true;
}
// Zero by itself with no other piece of number.
if (stream.match(/^-?0(?![\dx])/i)) {
intLiteral = true;
}
if (intLiteral) {
return 'number';
}
}
// Handle strings
if (stream.match(stringPrefixes)) {
state.tokenize = tokenFactory(stream.current(), 'string');
return state.tokenize(stream, state);
}
// Handle regex literals
if (stream.match(regexPrefixes)) {
if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division
state.tokenize = tokenFactory(stream.current(), 'string-2');
return state.tokenize(stream, state);
} else {
stream.backUp(1);
}
}
// Handle operators and delimiters
if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
return 'punctuation';
}
if (stream.match(doubleOperators)
|| stream.match(singleOperators)
|| stream.match(wordOperators)) {
return 'operator';
}
if (stream.match(singleDelimiters)) {
return 'punctuation';
}
if (stream.match(constants)) {
return 'atom';
}
if (stream.match(keywords)) {
return 'keyword';
}
if (stream.match(identifiers)) {
return 'variable';
}
// Handle non-detected items
stream.next();
return ERRORCLASS;
}
function tokenFactory(delimiter, outclass) {
var singleline = delimiter.length == 1;
return function tokenString(stream, state) {
while (!stream.eol()) {
stream.eatWhile(/[^'"\/\\]/);
if (stream.eat('\\')) {
stream.next();
if (singleline && stream.eol()) {
return outclass;
}
} else if (stream.match(delimiter)) {
state.tokenize = tokenBase;
return outclass;
} else {
stream.eat(/['"\/]/);
}
}
if (singleline) {
if (conf.mode.singleLineStringErrors) {
outclass = ERRORCLASS
} else {
state.tokenize = tokenBase;
}
}
return outclass;
};
}
function longComment(stream, state) {
while (!stream.eol()) {
stream.eatWhile(/[^#]/);
if (stream.match("###")) {
state.tokenize = tokenBase;
break;
}
stream.eatWhile("#");
}
return "comment"
}
function indent(stream, state, type) {
type = type || 'coffee';
var indentUnit = 0;
if (type === 'coffee') {
for (var i = 0; i < state.scopes.length; i++) {
if (state.scopes[i].type === 'coffee') {
indentUnit = state.scopes[i].offset + conf.indentUnit;
break;
}
}
} else {
indentUnit = stream.column() + stream.current().length;
}
state.scopes.unshift({
offset: indentUnit,
type: type
});
}
function dedent(stream, state) {
if (state.scopes.length == 1) return;
if (state.scopes[0].type === 'coffee') {
var _indent = stream.indentation();
var _indent_index = -1;
for (var i = 0; i < state.scopes.length; ++i) {
if (_indent === state.scopes[i].offset) {
_indent_index = i;
break;
}
}
if (_indent_index === -1) {
return true;
}
while (state.scopes[0].offset !== _indent) {
state.scopes.shift();
}
return false
} else {
state.scopes.shift();
return false;
}
}
function tokenLexer(stream, state) {
var style = state.tokenize(stream, state);
var current = stream.current();
// Handle '.' connected identifiers
if (current === '.') {
style = state.tokenize(stream, state);
current = stream.current();
if (style === 'variable') {
return 'variable';
} else {
return ERRORCLASS;
}
}
// Handle properties
if (current === '@') {
stream.eat('@');
return 'keyword';
}
// Handle scope changes.
if (current === 'return') {
state.dedent += 1;
}
if (((current === '->' || current === '=>') &&
!state.lambda &&
state.scopes[0].type == 'coffee' &&
stream.peek() === '')
|| style === 'indent') {
indent(stream, state);
}
var delimiter_index = '[({'.indexOf(current);
if (delimiter_index !== -1) {
indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
}
if (indentKeywords.exec(current)){
indent(stream, state);
}
if (current == 'then'){
dedent(stream, state);
}
if (style === 'dedent') {
if (dedent(stream, state)) {
return ERRORCLASS;
}
}
delimiter_index = '])}'.indexOf(current);
if (delimiter_index !== -1) {
if (dedent(stream, state)) {
return ERRORCLASS;
}
}
if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') {
if (state.scopes.length > 1) state.scopes.shift();
state.dedent -= 1;
}
return style;
}
var external = {
startState: function(basecolumn) {
return {
tokenize: tokenBase,
scopes: [{offset:basecolumn || 0, type:'coffee'}],
lastToken: null,
lambda: false,
dedent: 0
};
},
token: function(stream, state) {
var style = tokenLexer(stream, state);
state.lastToken = {style:style, content: stream.current()};
if (stream.eol() && stream.lambda) {
state.lambda = false;
}
return style;
},
indent: function(state, textAfter) {
if (state.tokenize != tokenBase) {
return 0;
}
return state.scopes[0].offset;
}
};
return external;
});
CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript');

View File

@@ -0,0 +1,727 @@
<!doctype html>
<html>
<head>
<title>CodeMirror: CoffeeScript mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="coffeescript.js"></script>
<style>.CodeMirror {border-top: 1px solid silver; border-bottom: 1px solid silver;}</style>
<link rel="stylesheet" href="../../doc/docs.css">
</head>
<body>
<h1>CodeMirror: CoffeeScript mode</h1>
<form><textarea id="code" name="code">
# CoffeeScript mode for CodeMirror
# Copyright (c) 2011 Jeff Pickhardt, released under
# the MIT License.
#
# Modified from the Python CodeMirror mode, which also is
# under the MIT License Copyright (c) 2010 Timothy Farrell.
#
# The following script, Underscore.coffee, is used to
# demonstrate CoffeeScript mode for CodeMirror.
#
# To download CoffeeScript mode for CodeMirror, go to:
# https://github.com/pickhardt/coffeescript-codemirror-mode
# **Underscore.coffee
# (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.**
# Underscore is freely distributable under the terms of the
# [MIT license](http://en.wikipedia.org/wiki/MIT_License).
# Portions of Underscore are inspired by or borrowed from
# [Prototype.js](http://prototypejs.org/api), Oliver Steele's
# [Functional](http://osteele.com), and John Resig's
# [Micro-Templating](http://ejohn.org).
# For all details and documentation:
# http://documentcloud.github.com/underscore/
# Baseline setup
# --------------
# Establish the root object, `window` in the browser, or `global` on the server.
root = this
# Save the previous value of the `_` variable.
previousUnderscore = root._
### Multiline
comment
###
# Establish the object that gets thrown to break out of a loop iteration.
# `StopIteration` is SOP on Mozilla.
breaker = if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
#### Docco style single line comment (title)
# Helper function to escape **RegExp** contents, because JS doesn't have one.
escapeRegExp = (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
# Save bytes in the minified (but not gzipped) version:
ArrayProto = Array.prototype
ObjProto = Object.prototype
# Create quick reference variables for speed access to core prototypes.
slice = ArrayProto.slice
unshift = ArrayProto.unshift
toString = ObjProto.toString
hasOwnProperty = ObjProto.hasOwnProperty
propertyIsEnumerable = ObjProto.propertyIsEnumerable
# All **ECMA5** native implementations we hope to use are declared here.
nativeForEach = ArrayProto.forEach
nativeMap = ArrayProto.map
nativeReduce = ArrayProto.reduce
nativeReduceRight = ArrayProto.reduceRight
nativeFilter = ArrayProto.filter
nativeEvery = ArrayProto.every
nativeSome = ArrayProto.some
nativeIndexOf = ArrayProto.indexOf
nativeLastIndexOf = ArrayProto.lastIndexOf
nativeIsArray = Array.isArray
nativeKeys = Object.keys
# Create a safe reference to the Underscore object for use below.
_ = (obj) -> new wrapper(obj)
# Export the Underscore object for **CommonJS**.
if typeof(exports) != 'undefined' then exports._ = _
# Export Underscore to global scope.
root._ = _
# Current version.
_.VERSION = '1.1.0'
# Collection Functions
# --------------------
# The cornerstone, an **each** implementation.
# Handles objects implementing **forEach**, arrays, and raw objects.
_.each = (obj, iterator, context) ->
try
if nativeForEach and obj.forEach is nativeForEach
obj.forEach iterator, context
else if _.isNumber obj.length
iterator.call context, obj[i], i, obj for i in [0...obj.length]
else
iterator.call context, val, key, obj for own key, val of obj
catch e
throw e if e isnt breaker
obj
# Return the results of applying the iterator to each element. Use JavaScript
# 1.6's version of **map**, if possible.
_.map = (obj, iterator, context) ->
return obj.map(iterator, context) if nativeMap and obj.map is nativeMap
results = []
_.each obj, (value, index, list) ->
results.push iterator.call context, value, index, list
results
# **Reduce** builds up a single result from a list of values. Also known as
# **inject**, or **foldl**. Uses JavaScript 1.8's version of **reduce**, if possible.
_.reduce = (obj, iterator, memo, context) ->
if nativeReduce and obj.reduce is nativeReduce
iterator = _.bind iterator, context if context
return obj.reduce iterator, memo
_.each obj, (value, index, list) ->
memo = iterator.call context, memo, value, index, list
memo
# The right-associative version of **reduce**, also known as **foldr**. Uses
# JavaScript 1.8's version of **reduceRight**, if available.
_.reduceRight = (obj, iterator, memo, context) ->
if nativeReduceRight and obj.reduceRight is nativeReduceRight
iterator = _.bind iterator, context if context
return obj.reduceRight iterator, memo
reversed = _.clone(_.toArray(obj)).reverse()
_.reduce reversed, iterator, memo, context
# Return the first value which passes a truth test.
_.detect = (obj, iterator, context) ->
result = null
_.each obj, (value, index, list) ->
if iterator.call context, value, index, list
result = value
_.breakLoop()
result
# Return all the elements that pass a truth test. Use JavaScript 1.6's
# **filter**, if it exists.
_.filter = (obj, iterator, context) ->
return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter
results = []
_.each obj, (value, index, list) ->
results.push value if iterator.call context, value, index, list
results
# Return all the elements for which a truth test fails.
_.reject = (obj, iterator, context) ->
results = []
_.each obj, (value, index, list) ->
results.push value if not iterator.call context, value, index, list
results
# Determine whether all of the elements match a truth test. Delegate to
# JavaScript 1.6's **every**, if it is present.
_.every = (obj, iterator, context) ->
iterator ||= _.identity
return obj.every iterator, context if nativeEvery and obj.every is nativeEvery
result = true
_.each obj, (value, index, list) ->
_.breakLoop() unless (result = result and iterator.call(context, value, index, list))
result
# Determine if at least one element in the object matches a truth test. Use
# JavaScript 1.6's **some**, if it exists.
_.some = (obj, iterator, context) ->
iterator ||= _.identity
return obj.some iterator, context if nativeSome and obj.some is nativeSome
result = false
_.each obj, (value, index, list) ->
_.breakLoop() if (result = iterator.call(context, value, index, list))
result
# Determine if a given value is included in the array or object,
# based on `===`.
_.include = (obj, target) ->
return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf
return true for own key, val of obj when val is target
false
# Invoke a method with arguments on every item in a collection.
_.invoke = (obj, method) ->
args = _.rest arguments, 2
(if method then val[method] else val).apply(val, args) for val in obj
# Convenience version of a common use case of **map**: fetching a property.
_.pluck = (obj, key) ->
_.map(obj, (val) -> val[key])
# Return the maximum item or (item-based computation).
_.max = (obj, iterator, context) ->
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
result = computed: -Infinity
_.each obj, (value, index, list) ->
computed = if iterator then iterator.call(context, value, index, list) else value
computed >= result.computed and (result = {value: value, computed: computed})
result.value
# Return the minimum element (or element-based computation).
_.min = (obj, iterator, context) ->
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
result = computed: Infinity
_.each obj, (value, index, list) ->
computed = if iterator then iterator.call(context, value, index, list) else value
computed < result.computed and (result = {value: value, computed: computed})
result.value
# Sort the object's values by a criterion produced by an iterator.
_.sortBy = (obj, iterator, context) ->
_.pluck(((_.map obj, (value, index, list) ->
{value: value, criteria: iterator.call(context, value, index, list)}
).sort((left, right) ->
a = left.criteria; b = right.criteria
if a < b then -1 else if a > b then 1 else 0
)), 'value')
# Use a comparator function to figure out at what index an object should
# be inserted so as to maintain order. Uses binary search.
_.sortedIndex = (array, obj, iterator) ->
iterator ||= _.identity
low = 0
high = array.length
while low < high
mid = (low + high) >> 1
if iterator(array[mid]) < iterator(obj) then low = mid + 1 else high = mid
low
# Convert anything iterable into a real, live array.
_.toArray = (iterable) ->
return [] if (!iterable)
return iterable.toArray() if (iterable.toArray)
return iterable if (_.isArray(iterable))
return slice.call(iterable) if (_.isArguments(iterable))
_.values(iterable)
# Return the number of elements in an object.
_.size = (obj) -> _.toArray(obj).length
# Array Functions
# ---------------
# Get the first element of an array. Passing `n` will return the first N
# values in the array. Aliased as **head**. The `guard` check allows it to work
# with **map**.
_.first = (array, n, guard) ->
if n and not guard then slice.call(array, 0, n) else array[0]
# Returns everything but the first entry of the array. Aliased as **tail**.
# Especially useful on the arguments object. Passing an `index` will return
# the rest of the values in the array from that index onward. The `guard`
# check allows it to work with **map**.
_.rest = (array, index, guard) ->
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
# Get the last element of an array.
_.last = (array) -> array[array.length - 1]
# Trim out all falsy values from an array.
_.compact = (array) -> item for item in array when item
# Return a completely flattened version of an array.
_.flatten = (array) ->
_.reduce array, (memo, value) ->
return memo.concat(_.flatten(value)) if _.isArray value
memo.push value
memo
, []
# Return a version of the array that does not contain the specified value(s).
_.without = (array) ->
values = _.rest arguments
val for val in _.toArray(array) when not _.include values, val
# Produce a duplicate-free version of the array. If the array has already
# been sorted, you have the option of using a faster algorithm.
_.uniq = (array, isSorted) ->
memo = []
for el, i in _.toArray array
memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
memo
# Produce an array that contains every item shared between all the
# passed-in arrays.
_.intersect = (array) ->
rest = _.rest arguments
_.select _.uniq(array), (item) ->
_.all rest, (other) ->
_.indexOf(other, item) >= 0
# Zip together multiple lists into a single array -- elements that share
# an index go together.
_.zip = ->
length = _.max _.pluck arguments, 'length'
results = new Array length
for i in [0...length]
results[i] = _.pluck arguments, String i
results
# If the browser doesn't supply us with **indexOf** (I'm looking at you, MSIE),
# we need this function. Return the position of the first occurrence of an
# item in an array, or -1 if the item is not included in the array.
_.indexOf = (array, item) ->
return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf
i = 0; l = array.length
while l - i
if array[i] is item then return i else i++
-1
# Provide JavaScript 1.6's **lastIndexOf**, delegating to the native function,
# if possible.
_.lastIndexOf = (array, item) ->
return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf
i = array.length
while i
if array[i] is item then return i else i--
-1
# Generate an integer Array containing an arithmetic progression. A port of
# [the native Python **range** function](http://docs.python.org/library/functions.html#range).
_.range = (start, stop, step) ->
a = arguments
solo = a.length <= 1
i = start = if solo then 0 else a[0]
stop = if solo then a[0] else a[1]
step = a[2] or 1
len = Math.ceil((stop - start) / step)
return [] if len <= 0
range = new Array len
idx = 0
loop
return range if (if step > 0 then i - stop else stop - i) >= 0
range[idx] = i
idx++
i+= step
# Function Functions
# ------------------
# Create a function bound to a given object (assigning `this`, and arguments,
# optionally). Binding with arguments is also known as **curry**.
_.bind = (func, obj) ->
args = _.rest arguments, 2
-> func.apply obj or root, args.concat arguments
# Bind all of an object's methods to that object. Useful for ensuring that
# all callbacks defined on an object belong to it.
_.bindAll = (obj) ->
funcs = if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
_.each funcs, (f) -> obj[f] = _.bind obj[f], obj
obj
# Delays a function for the given number of milliseconds, and then calls
# it with the arguments supplied.
_.delay = (func, wait) ->
args = _.rest arguments, 2
setTimeout((-> func.apply(func, args)), wait)
# Memoize an expensive function by storing its results.
_.memoize = (func, hasher) ->
memo = {}
hasher or= _.identity
->
key = hasher.apply this, arguments
return memo[key] if key of memo
memo[key] = func.apply this, arguments
# Defers a function, scheduling it to run after the current call stack has
# cleared.
_.defer = (func) ->
_.delay.apply _, [func, 1].concat _.rest arguments
# Returns the first function passed as an argument to the second,
# allowing you to adjust arguments, run code before and after, and
# conditionally execute the original function.
_.wrap = (func, wrapper) ->
-> wrapper.apply wrapper, [func].concat arguments
# Returns a function that is the composition of a list of functions, each
# consuming the return value of the function that follows.
_.compose = ->
funcs = arguments
->
args = arguments
for i in [funcs.length - 1..0] by -1
args = [funcs[i].apply(this, args)]
args[0]
# Object Functions
# ----------------
# Retrieve the names of an object's properties.
_.keys = nativeKeys or (obj) ->
return _.range 0, obj.length if _.isArray(obj)
key for key, val of obj
# Retrieve the values of an object's properties.
_.values = (obj) ->
_.map obj, _.identity
# Return a sorted list of the function names available in Underscore.
_.functions = (obj) ->
_.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
# Extend a given object with all of the properties in a source object.
_.extend = (obj) ->
for source in _.rest(arguments)
obj[key] = val for key, val of source
obj
# Create a (shallow-cloned) duplicate of an object.
_.clone = (obj) ->
return obj.slice 0 if _.isArray obj
_.extend {}, obj
# Invokes interceptor with the obj, and then returns obj.
# The primary purpose of this method is to "tap into" a method chain,
# in order to perform operations on intermediate results within
the chain.
_.tap = (obj, interceptor) ->
interceptor obj
obj
# Perform a deep comparison to check if two objects are equal.
_.isEqual = (a, b) ->
# Check object identity.
return true if a is b
# Different types?
atype = typeof(a); btype = typeof(b)
return false if atype isnt btype
# Basic equality test (watch out for coercions).
return true if `a == b`
# One is falsy and the other truthy.
return false if (!a and b) or (a and !b)
# One of them implements an `isEqual()`?
return a.isEqual(b) if a.isEqual
# Check dates' integer values.
return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
# Both are NaN?
return false if _.isNaN(a) and _.isNaN(b)
# Compare regular expressions.
if _.isRegExp(a) and _.isRegExp(b)
return a.source is b.source and
a.global is b.global and
a.ignoreCase is b.ignoreCase and
a.multiline is b.multiline
# If a is not an object by this point, we can't handle it.
return false if atype isnt 'object'
# Check for different array lengths before comparing contents.
return false if a.length and (a.length isnt b.length)
# Nothing else worked, deep compare the contents.
aKeys = _.keys(a); bKeys = _.keys(b)
# Different object sizes?
return false if aKeys.length isnt bKeys.length
# Recursive comparison of contents.
return false for key, val of a when !(key of b) or !_.isEqual(val, b[key])
true
# Is a given array or object empty?
_.isEmpty = (obj) ->
return obj.length is 0 if _.isArray(obj) or _.isString(obj)
return false for own key of obj
true
# Is a given value a DOM element?
_.isElement = (obj) -> obj and obj.nodeType is 1
# Is a given value an array?
_.isArray = nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift and not obj.callee)
# Is a given variable an arguments object?
_.isArguments = (obj) -> obj and obj.callee
# Is the given value a function?
_.isFunction = (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)
# Is the given value a string?
_.isString = (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
# Is a given value a number?
_.isNumber = (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
# Is a given value a boolean?
_.isBoolean = (obj) -> obj is true or obj is false
# Is a given value a Date?
_.isDate = (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
# Is the given value a regular expression?
_.isRegExp = (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
# Is the given value NaN -- this one is interesting. `NaN != NaN`, and
# `isNaN(undefined) == true`, so we make sure it's a number first.
_.isNaN = (obj) -> _.isNumber(obj) and window.isNaN(obj)
# Is a given value equal to null?
_.isNull = (obj) -> obj is null
# Is a given variable undefined?
_.isUndefined = (obj) -> typeof obj is 'undefined'
# Utility Functions
# -----------------
# Run Underscore.js in noConflict mode, returning the `_` variable to its
# previous owner. Returns a reference to the Underscore object.
_.noConflict = ->
root._ = previousUnderscore
this
# Keep the identity function around for default iterators.
_.identity = (value) -> value
# Run a function `n` times.
_.times = (n, iterator, context) ->
iterator.call context, i for i in [0...n]
# Break out of the middle of an iteration.
_.breakLoop = -> throw breaker
# Add your own custom functions to the Underscore object, ensuring that
# they're correctly added to the OOP wrapper as well.
_.mixin = (obj) ->
for name in _.functions(obj)
addToWrapper name, _[name] = obj[name]
# Generate a unique integer id (unique within the entire client session).
# Useful for temporary DOM ids.
idCounter = 0
_.uniqueId = (prefix) ->
(prefix or '') + idCounter++
# By default, Underscore uses **ERB**-style template delimiters, change the
# following template settings to use alternative delimiters.
_.templateSettings = {
start: '<%'
end: '%>'
interpolate: /<%=(.+?)%>/g
}
# JavaScript templating a-la **ERB**, pilfered from John Resig's
# *Secrets of the JavaScript Ninja*, page 83.
# Single-quote fix from Rick Strahl.
# With alterations for arbitrary delimiters, and to preserve whitespace.
_.template = (str, data) ->
c = _.templateSettings
endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
fn = new Function 'obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj||{}){p.push(\'' +
str.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
.replace(endMatch,"<22><><EFBFBD>")
.split("'").join("\\'")
.split("<22><><EFBFBD>").join("'")
.replace(c.interpolate, "',$1,'")
.split(c.start).join("');")
.split(c.end).join("p.push('") +
"');}return p.join('');"
if data then fn(data) else fn
# Aliases
# -------
_.forEach = _.each
_.foldl = _.inject = _.reduce
_.foldr = _.reduceRight
_.select = _.filter
_.all = _.every
_.any = _.some
_.contains = _.include
_.head = _.first
_.tail = _.rest
_.methods = _.functions
# Setup the OOP Wrapper
# ---------------------
# If Underscore is called as a function, it returns a wrapped object that
# can be used OO-style. This wrapper holds altered versions of all the
# underscore functions. Wrapped objects may be chained.
wrapper = (obj) ->
this._wrapped = obj
this
# Helper function to continue chaining intermediate results.
result = (obj, chain) ->
if chain then _(obj).chain() else obj
# A method to easily add functions to the OOP wrapper.
addToWrapper = (name, func) ->
wrapper.prototype[name] = ->
args = _.toArray arguments
unshift.call args, this._wrapped
result func.apply(_, args), this._chain
# Add all ofthe Underscore functions to the wrapper object.
_.mixin _
# Add all mutator Array functions to the wrapper.
_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
method = Array.prototype[name]
wrapper.prototype[name] = ->
method.apply(this._wrapped, arguments)
result(this._wrapped, this._chain)
# Add all accessor Array functions to the wrapper.
_.each ['concat', 'join', 'slice'], (name) ->
method = Array.prototype[name]
wrapper.prototype[name] = ->
result(method.apply(this._wrapped, arguments), this._chain)
# Start chaining a wrapped Underscore object.
wrapper::chain = ->
this._chain = true
this
# Extracts the result from a wrapped and chained object.
wrapper::value = -> this._wrapped
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
</script>
<p><strong>MIME types defined:</strong> <code>text/x-coffeescript</code>.</p>
<p>The CoffeeScript mode was written by Jeff Pickhardt (<a href="LICENSE">license</a>).</p>
</body>
</html>

View File

@@ -54,7 +54,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
}
else if (/\d/.test(ch)) {
else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
return ret("number", "number");
}

View File

@@ -0,0 +1,21 @@
.cm-s-erlang-dark { background: #002240; color: white; }
.cm-s-erlang-dark div.CodeMirror-selected { background: #b36539 !important; }
.cm-s-erlang-dark .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; }
.cm-s-erlang-dark .CodeMirror-gutter-text { color: #d0d0d0; }
.cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white !important; }
.cm-s-erlang-dark span.cm-atom { color: #845dc4; }
.cm-s-erlang-dark span.cm-attribute { color: #ff80e1; }
.cm-s-erlang-dark span.cm-bracket { color: #ff9d00; }
.cm-s-erlang-dark span.cm-builtin { color: #eeaaaa; }
.cm-s-erlang-dark span.cm-comment { color: #7777ff; }
.cm-s-erlang-dark span.cm-def { color: #ee77aa; }
.cm-s-erlang-dark span.cm-error { color: #9d1e15; }
.cm-s-erlang-dark span.cm-keyword { color: #ffee80; }
.cm-s-erlang-dark span.cm-meta { color: #50fefe; }
.cm-s-erlang-dark span.cm-number { color: #ffd0d0; }
.cm-s-erlang-dark span.cm-operator { color: #dd1111; }
.cm-s-erlang-dark span.cm-string { color: #3ad900; }
.cm-s-erlang-dark span.cm-tag { color: #9effff; }
.cm-s-erlang-dark span.cm-variable { color: #50fe50; }
.cm-s-erlang-dark span.cm-variable-2 { color: #ee00ee; }

30
LICENSE.md Normal file
View File

@@ -0,0 +1,30 @@
Copyright (C) 2012 Matt Pass
Website: mattpass.com
Email: matt@mattpass.com
Twitter: @mattpass
#ICEcoder License
##Standard Open Source Initiative MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
```
Please note that this LICENSE file covers the ICEcoder shell IDE only and 3rd
party code, in subfolders such as CodeMirror and plugins, may contain their
own LICENSE files, and may not also be an MIT licence.
```

View File

@@ -8,6 +8,7 @@
<script src="<?php echo $codeMirrorDir; ?>/lib/codemirror.js"></script>
<script src="<?php echo $codeMirrorDir; ?>/mode/xml/xml.js"></script>
<script src="<?php echo $codeMirrorDir; ?>/mode/javascript/javascript.js"></script>
<script src="<?php echo $codeMirrorDir; ?>/mode/coffeescript/coffeescript.js"></script>
<script src="<?php echo $codeMirrorDir; ?>/mode/css/css.js"></script>
<script src="<?php echo $codeMirrorDir; ?>/mode/clike/clike.js"></script>
<script src="<?php echo $codeMirrorDir; ?>/mode/php/php.js"></script>
@@ -23,15 +24,15 @@ if ($theme=="default") {
}
?>
<style type="text/css">
.CodeMirror {position: absolute; width: 0px; background-color: #ffffff}
.CodeMirror {position: absolute; width: 0px; background-color: #fff}
.CodeMirror-scroll {width: 100px; height: 100px;}
.cm-s-visible {display: block; top: 0px}
.cm-s-hidden {display: none; top: 4000px}
.cm-s-activeLine {background: #002 !important;}
// Make sure this next one remains the 5th item, updated with JS
.cm-tab:after {position: relative; display: inline-block; width: 0px; left: -1.4em; overflow: visible; color: #aaa; content: "<?php if ($visibleTabs) {?>\21e5<?;};?>";}
span.CodeMirror-matchhighlight {background: #555555}
.CodeMirror-focused span.CodeMirror-matchhighlight {color: #000000; background: #555555; !important}
span.CodeMirror-matchhighlight {background: #555}
.CodeMirror-focused span.CodeMirror-matchhighlight {color: #000; background: #555; !important}
</style>
</head>
@@ -40,8 +41,8 @@ span.CodeMirror-matchhighlight {background: #555555}
<script>
function createNewCMInstance(num) {
var fileName = top.ICEcoder.openFiles[top.ICEcoder.selectedTab-1];
var codeFold = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder,'<span style=\"display: inline-block; width: 13px; height: 13px; background-color: #bb0000; color: #ffffff; text-align: center; cursor: pointer\"><span style="position: relative; top: -1px">+</span></span> %N%');
var codeFold_JS_PHP_Ruby = CodeMirror.newFoldFunction(CodeMirror.braceRangeFinder,'<span style=\"display: inline-block; width: 13px; height: 13px; background-color: #bb0000; color: #ffffff; text-align: center; cursor: pointer\"><span style="position: relative; top: -1px">+</span></span> %N%');
var codeFold = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder,'<span style=\"display: inline-block; width: 13px; height: 13px; background-color: #b00; color: #fff; text-align: center; cursor: pointer\"><span style="position: relative; top: -1px">+</span></span> %N%');
var codeFold_JS_Coffee_PHP_Ruby = CodeMirror.newFoldFunction(CodeMirror.braceRangeFinder,'<span style=\"display: inline-block; width: 13px; height: 13px; background-color: #b00; color: #fff; text-align: center; cursor: pointer\"><span style="position: relative; top: -1px">+</span></span> %N%');
window['cM'+num] = CodeMirror(document.body, {
mode: "application/x-httpd-php",
@@ -122,7 +123,7 @@ function createNewCMInstance(num) {
};
lastKeyCode = e.keyCode;
},
onGutterClick: !fileName || (fileName && fileName.indexOf(".js") == -1 && fileName.indexOf(".php") && fileName.indexOf(".rb") == -1) ? codeFold : codeFold_JS_PHP_Ruby,
onGutterClick: !fileName || (fileName && fileName.indexOf(".js") == -1 && fileName.indexOf(".coffee") == -1 && fileName.indexOf(".php") && fileName.indexOf(".rb") == -1) ? codeFold : codeFold_JS_Coffee_PHP_Ruby,
extraKeys: {
"Tab": function(cm) {CodeMirror.commands[top.tabsIndent ? "defaultTab" : "insertTab"](cm);},
"Shift-Tab": "indentLess"

View File

@@ -54,7 +54,7 @@ function fileManager_dir($directory, $return_link, $first_call=true) {
if ($serverType=="Linux") {
$chmodInfo = substr(sprintf('%o', fileperms($link)), -4);
$fileAtts = '<span style="color: #888888; font-size: 8px">'.$chmodInfo.'</span>';
$fileAtts = '<span style="color: #888; font-size: 8px">'.$chmodInfo.'</span>';
}
$fileManager = "<ul class=\"fileManager\">";
$fileManager .= "<li class=\"pft-directory\"><a href=\"#\" onMouseOver=\"top.ICEcoder.overFileFolder('folder','$link')\" onMouseOut=\"top.ICEcoder.overFileFolder('folder','')\" style=\"position: relative; left:-22px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span id=\"|\">/ [ROOT]</span> ".$fileAtts."</a>";
@@ -88,14 +88,14 @@ function fileManager_dir($directory, $return_link, $first_call=true) {
$fileAtts = "";
if ($serverType=="Linux") {
$chmodInfo = substr(sprintf('%o', fileperms($link)), -4);
$fileAtts = '<span style="color: #888888; font-size: 8px">'.$chmodInfo.'</span>';
$fileAtts = '<span style="color: #888; font-size: 8px">'.$chmodInfo.'</span>';
}
if ($_SESSION['userLevel'] == 10 || ($_SESSION['userLevel'] < 10 && $restrictedFile==false)) {
$fileManager .= "<li class=\"pft-directory\"><a href=\"#\" onMouseOver=\"top.ICEcoder.overFileFolder('folder','$link')\" onMouseOut=\"top.ICEcoder.overFileFolder('folder','')\" style=\"position: relative; left:-22px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span id=\"".str_replace("/","|",str_replace($docRoot,"",$link))."\">" . htmlspecialchars($this_file) . "</span> ".$fileAtts."</a>";
$fileManager .= fileManager_dir("$directory/$this_file", $return_link , false);
$fileManager .= "</li>";
} else {
$fileManager .= "<li class=\"pft-directory\" style=\"cursor: pointer\"><span style=\"position: relative; left:-22px; color: #888888\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [HIDDEN] ".$fileAtts."</span></li>";
$fileManager .= "<li class=\"pft-directory\" style=\"cursor: pointer\"><span style=\"position: relative; left:-22px; color: #888\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [HIDDEN] ".$fileAtts."</span></li>";
}
} else {
// File
@@ -115,12 +115,12 @@ function fileManager_dir($directory, $return_link, $first_call=true) {
$fileAtts = "";
if ($serverType=="Linux") {
$chmodInfo = substr(sprintf('%o', fileperms($link)), -4);
$fileAtts = '<span style="color: #888888; font-size: 8px">'.$chmodInfo.'</span>';
$fileAtts = '<span style="color: #888; font-size: 8px">'.$chmodInfo.'</span>';
}
$fileManager .= "<li class=\"pft-file " . strtolower($ext) . "\"><a nohref onMouseOver=\"top.ICEcoder.overFileFolder('file','$link')\" onMouseOut=\"top.ICEcoder.overFileFolder('file','')\" style=\"position: relative; left:-22px; cursor: pointer\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span id=\"".str_replace("/","|",str_replace($docRoot,"",$link))."\">" . htmlspecialchars($this_file) . "</span> ".$fileAtts."</a></li>";
} else {
$fileAtts = "<img src=\"images/file-manager-icons/padlock.png\" style=\"cursor: pointer\" onClick=\"alert('Sorry, you need higher admin level rights to view.')\">";
$fileManager .= "<li class=\"pft-file " . strtolower($ext) . "\" style=\"cursor: default\"><span style=\"position: relative; left:-22px; color: #888888\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [HIDDEN] ".$fileAtts."</span></li>";
$fileManager .= "<li class=\"pft-file " . strtolower($ext) . "\" style=\"cursor: default\"><span style=\"position: relative; left:-22px; color: #888\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [HIDDEN] ".$fileAtts."</span></li>";
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -22,15 +22,15 @@ body {overflow: hidden;
-moz-user-select: none;
-o-user-select:none;
user-select: none;
background-color: #222222;
background-color: #222;
}
h1 {font-size: 36px; font-weight: normal; color: #888888; margin-bottom: 20px}
h2 {font-size: 18px; font-weight: normal; color: #ffffff}
h1 {font-size: 36px; font-weight: normal; color: #888; margin-bottom: 20px}
h2 {font-size: 18px; font-weight: normal; color: #fff}
.blackMask {position: fixed; display: table; width: 100%; height: 100%; top: 0px; left: 0px; visibility: hidden; background-color: rgba(0,0,0,0.8); text-align: center; z-index: 100}
.blackMask .popupVCenter {#position: absolute; display: table-cell; #top: 50%; vertical-align: middle; text-align: center}
.popupVCenter .popup {#position: relative; #top: -50%; text-align: center; color: #ffffff; font-size: 10px}
.popupVCenter .popup {#position: relative; #top: -50%; text-align: center; color: #fff; font-size: 10px}
.whiteGlow {
-webkit-box-shadow: 0px 0px 8px 2px rgba(255,255,255,0.6);
-moz-box-shadow: 0px 0px 8px 2px rgba(255,255,255,0.6);
@@ -72,20 +72,20 @@ h2 {font-size: 18px; font-weight: normal; color: #ffffff}
100%{ width:100%;}
}
.header {position: absolute; display: inline-block; width: 100%; height: 40px; background-color: #ffffff; text-align: right; z-index: 2}
.header {position: absolute; display: inline-block; width: 100%; height: 40px; background-color: #fff; text-align: right; z-index: 2}
.header .plugins {position: absolute; display: inline-block; left: 15px; top: 3px}
.header .plugins img {position: relative; display: inline-block; margin-right: 15px}
.header .version {position: relative; display: inline-block; margin-top: 25px; font-size: 10px; color: #bbbbbb}
.header .version {position: relative; display: inline-block; margin-top: 25px; font-size: 10px; color: #bbb}
.header .logo {position: relative; margin: 5px 10px 0px 5px; cursor: pointer}
.files {position: absolute; display: inline-block; height: 100%; width: 250px; background-color: #444444; background-image: url('../images/files-arrow.gif'); background-repeat: no-repeat; background-position: 100% 50%; overflow: hidden; z-index: 1;
.files {position: absolute; display: inline-block; height: 100%; width: 250px; background-color: #444; background-image: url('../images/files-arrow.gif'); background-repeat: no-repeat; background-position: 100% 50%; overflow: hidden; z-index: 1;
-webkit-box-shadow: 0px 0px 10px 4px rgba(0,0,0,0.4);
-moz-box-shadow: 0px 0px 10px 4px rgba(0,0,0,0.4);
box-shadow: 0px 0px 10px 4px rgba(0,0,0,0.4);
}
.files .account {display: inline-block; height: 50px; width: 250px; margin-top: 40px; background-color: #888888}
.files .account {display: inline-block; height: 50px; width: 250px; margin-top: 40px; background-color: #888}
.files .accountLoginContainer {position: absolute; width: 250px; height: 50px; z-index: 1}
.files .accountLoginContainer .accountLogin {position: absolute; width: 250px; height: 50px; top: 0px; background-color: #666666;
.files .accountLoginContainer .accountLogin {position: absolute; width: 250px; height: 50px; top: 0px; background-color: #666;
-webkit-transition: top 0.3s;
-moz-transition: top 0.3s;
-o-transition: top 0.3s;
@@ -93,15 +93,15 @@ h2 {font-size: 18px; font-weight: normal; color: #ffffff}
}
.files .accountOptions {position: relative; height: 31px; width: 200px; margin-left: 15px; margin-top: 8px}
.files .accountOptions img {cursor: pointer}
.files .accountPassword {position: relative; border: 1px solid #888888; background-color: #999999; height: 18px; width: 140px; margin-left: 14px; margin-top: 15px}
.files .accountPassword {position: relative; border: 1px solid #888; background-color: #999; height: 18px; width: 140px; margin-left: 14px; margin-top: 15px}
.files input:focus, .findReplace input:focus, .findReplace select:focus, .accountPassword:focus {
outline: none;
-webkit-box-shadow: 0px 0px 10px 1px rgba(0,198,255,0.7);
-moz-box-shadow: 0px 0px 10px 1px rgba(0,198,255,0.7);
box-shadow: 0px 0px 10px 1px rgba(0,198,255,0.7);
}
.files .button {position: absolute; border: 0px; background: #999999; color: #555555; height:20px; margin-top: 16px; margin-left: 5px; font-size: 11px; cursor: pointer}
.files .button:hover {background-color: #444444; color: #eeeeee}
.files .button {position: absolute; border: 0px; background: #999; color: #555; height:20px; margin-top: 16px; margin-left: 5px; font-size: 11px; cursor: pointer}
.files .button:hover {background-color: #444; color: #eee}
.files .lock {position: relative; margin-left: 225px; margin-top: -20px; z-index: 1}
.files .frame {display: inline-block; width: 250px}
.files .serverMessage {position: absolute; display: inline-block; width: 450px; bottom: 0px; margin-bottom: 30px; background-color: rgba(255,255,255,0.8); font-size: 10px; padding: 7px 12px; opacity: 0;
@@ -113,16 +113,16 @@ h2 {font-size: 18px; font-weight: normal; color: #ffffff}
.files .serverMessage b {font-size: 10px}
.editor {position: absolute; display: inline-block; top: 0px; left: 15px; width: 2400px}
.editor .tabsBar {display: inline-block; height: 22px; width: 2400px; margin-top: 40px; padding-left: 41px; background-color: #888888;}
.tabsBar .tab {display: inline-block; display: none; background-image: url('../images/nav-bg.gif'); background-repeat: repeat-x; background-position: 0px 0px; padding: 5px 8px 2px 8px; font-size: 10px; border-left: solid 1px #ffffff; border-right: solid 1px #777777; color: #ffffff; cursor: pointer;
.editor .tabsBar {display: inline-block; height: 22px; width: 2400px; margin-top: 40px; padding-left: 41px; background-color: #888;}
.tabsBar .tab {display: inline-block; display: none; background-image: url('../images/nav-bg.gif'); background-repeat: repeat-x; background-position: 0px 0px; padding: 5px 8px 2px 8px; font-size: 10px; border-left: solid 1px #fff; border-right: solid 1px #777; color: #fff; cursor: pointer;
-webkit-transition: all 0.15s;
-moz-transition: all 0.15s;
-o-transition: all 0.15s;
transition: all 0.15s;
}
.tabsBar .tab .closeTab {margin: 1px 0px 0px 5px; border-radius: 6px}
.tabsBar .newTab {display: inline-block; background-image: url('../images/nav-bg.gif'); background-repeat: repeat-x; background-position: 0px 0px; padding: 6px 5px 1px 5px; border-left: solid 1px #ffffff; border-right: solid 1px #777777; cursor: pointer;}
.editor .findBar {display: inline-block; height: 28px; width: 2400px; color: #ffffff; background-color: #141414}
.tabsBar .newTab {display: inline-block; background-image: url('../images/nav-bg.gif'); background-repeat: repeat-x; background-position: 0px 0px; padding: 6px 5px 1px 5px; border-left: solid 1px #fff; border-right: solid 1px #777; cursor: pointer;}
.editor .findBar {display: inline-block; height: 28px; width: 2400px; color: #fff; background-color: #141414}
.findBar .findReplace {position: absolute; z-index: 1}
.findReplace select {position: relative; font-size: 10px; margin: 8px 2px 0px 2px; top: -2px;}
.findReplace .findText {display: inline-block; height: 21px; font-size: 10px; margin: 8px 2px 0px 2px; margin-left: 43px}
@@ -132,7 +132,7 @@ h2 {font-size: 18px; font-weight: normal; color: #ffffff}
.findReplace .replaceText {height: 21px; font-size: 10px; margin: 8px 2px 0px 2px}
.findReplace .replace {position: relative; width: 120px; height: 16px; border: 0; top: -2px; font-size: 10px; padding-left: 5px}
.findReplace .targetText {height: 21px; font-size: 10px; margin: 8px 2px 0px 2px}
.findReplace .submit {position: relative; top: -2px; height: 17px; border: 1px solid #bbbbbb; background-color: #f8f8f8; font-size: 10px; cursor: pointer}
.findReplace .submit {position: relative; top: -2px; height: 17px; border: 1px solid #bbb; background-color: #f8f8f8; font-size: 10px; cursor: pointer}
.findReplace .results {position: relative; display: inline-block; width: 200px; height: 20px; font-size: 10px; margin: 8px 0px 0px 20px}
.findBar .codeAssist {position: fixed; display: inline-block; width: 100px; right: 74px; top: 70px; height: 21px; font-size: 10px; z-index: 1}
.findBar .codeAssist input {margin-top: -1px}
@@ -141,10 +141,10 @@ h2 {font-size: 18px; font-weight: normal; color: #ffffff}
.editor .code {position: relative; display: inline-block; top: 28px; width: 600px; height: 600px; visibility: hidden}
.footer {position: fixed; display: inline-block; width: 100%; height: 30px; bottom: 0px; background-color: rgba(0,0,0,0.7); left: 0px; z-index: 2}
.footer .nesting {display: inline-block; padding: 5px 8px; margin: 4px 0px 0px 15px; font-weight: bold; font-size: 10px; color: #ffffff; background-color: #00bb00}
.footer .nestLoc {position: absolute; display: inline-block; width: 120px; padding: 5px 0px 0px 8px; margin-top: 3px; left: 112px; font-weight: bold; font-size: 12px; color: #ffffff; text-align: right}
.footer .nestDisplay {position: absolute; display: inline-block; padding: 5px 0px 0px 8px; margin-top: 3px; left: 255px; font-size: 12px; color: #ffffff; text-align: right}
.footer .charDisplay {position: absolute; display: inline-block; padding: 5px 0px 0px 8px; margin-top: 3px; left: 100%; font-weight: bold; font-size: 12px; color: #ffffff; text-align: right; width: 200px; text-align: right; margin-left: -220px}
.footer .nesting {display: inline-block; padding: 5px 8px; margin: 4px 0px 0px 15px; font-weight: bold; font-size: 10px; color: #fff; background-color: #0b0}
.footer .nestLoc {position: absolute; display: inline-block; width: 120px; padding: 5px 0px 0px 8px; margin-top: 3px; left: 112px; font-weight: bold; font-size: 12px; color: #fff; text-align: right}
.footer .nestDisplay {position: absolute; display: inline-block; padding: 5px 0px 0px 8px; margin-top: 3px; left: 255px; font-size: 12px; color: #fff; text-align: right}
.footer .charDisplay {position: absolute; display: inline-block; padding: 5px 0px 0px 8px; margin-top: 3px; left: 100%; font-weight: bold; font-size: 12px; color: #fff; text-align: right; width: 200px; text-align: right; margin-left: -220px}
.textbox {
-webkit-box-shadow: inset 1px 1px 2px 0px rgba(0,0,0,0.4);
@@ -152,13 +152,13 @@ h2 {font-size: 18px; font-weight: normal; color: #ffffff}
box-shadow: inset 1px 1px 2px 0px rgba(0,0,0,0.4);
}
.fileMenu {position: absolute; display: none; left: 0px; top: 0px; background-color: #333333; z-index: 10}
.fileMenu a {display: block; padding: 2px 5px; background-color: #444444; color: #eeeeee; text-decoration: none}
.fileMenu a:hover {background-color: #666666}
.fileMenu {position: absolute; display: none; left: 0px; top: 0px; background-color: #333; z-index: 10}
.fileMenu a {display: block; padding: 2px 5px; background-color: #444; color: #eee; text-decoration: none}
.fileMenu a:hover {background-color: #666}
.screenContainer {position: absolute; display: table; width: 100%; height: 100%; top: 0px; left: 0px; text-align: center}
.screenContainer .screenVCenter {#position: absolute; display: table-cell; #top: 50%; vertical-align: middle; text-align: center}
.screenVCenter .screenCenter {#position: relative; #top: -50%; text-align: center; display: inline}
.screenCenter .version {position: relative; display: block; margin: 5px 0px 15px 0px; font-size: 10px; color: #bbbbbb}
.screenCenter .accountPassword {border: 1px solid #888888; height: 18px}
.screenCenter .button {border: 0px; background: #666666; color: #ffffff; height: 22px; cursor: pointer}
.screenCenter .version {position: relative; display: block; margin: 5px 0px 15px 0px; font-size: 10px; color: #bbb}
.screenCenter .accountPassword {border: 1px solid #888; height: 18px}
.screenCenter .button {border: 0px; background: #666; color: #fff; height: 22px; cursor: pointer}

View File

@@ -89,9 +89,9 @@ var ICEcoder = {
contentCleanUp: function() {
var fileName, cM, content;
// If it's not a JS, Ruby or CSS file, replace & and our temp </textarea> value
// If it's not a JS, CoffeeScript Ruby or CSS file, replace & and our temp </textarea> value
fileName = ICEcoder.openFiles[ICEcoder.selectedTab-1];
if (fileName.indexOf(".js")<0&&fileName.indexOf(".rb")&&fileName.indexOf(".css")<0) {
if (fileName.indexOf(".js")<0 && fileName.indexOf(".coffee")<0 && fileName.indexOf(".rb")<0 && fileName.indexOf(".css")<0) {
cM = ICEcoder.getcMInstance();
content = cM.getValue();
content = content.replace(/<ICEcoder:\/:textarea>/g,'</textarea>');
@@ -182,7 +182,7 @@ var ICEcoder = {
ICEcoder.nestDisplay.innerHTML = "";
if ("undefined" != typeof ICEcoder.openFiles[ICEcoder.selectedTab-1]) {
fileName = ICEcoder.openFiles[ICEcoder.selectedTab-1];
if (fileName.indexOf(".js")<0&&fileName.indexOf(".rb")<0&&fileName.indexOf(".css")<0&&
if (fileName.indexOf(".js")<0 && fileName.indexOf(".coffee")<0 && fileName.indexOf(".rb")<0 && fileName.indexOf(".css")<0&&
(nestCheck.indexOf("include(")==-1)&&(nestCheck.indexOf("include_once(")==-1)&&
(nestCheck.indexOf("<html")>-1||nestCheck.indexOf("<body")>-1)) {
@@ -363,6 +363,10 @@ var ICEcoder = {
// Redo our find display
top.ICEcoder.findMode = false;
ICEcoder.findReplace('find',true,false);
// Finally, update the cursor display
top.ICEcoder.getCaretPosition();
top.ICEcoder.updateCharDisplay();
}
},
@@ -372,9 +376,9 @@ var ICEcoder = {
for(var i=1;i<=ICEcoder.changedContent.length;i++) {
if (document.getElementById('closeTabButton'+i)) {
ICEcoder.changedContent[i-1]==1 ? document.getElementById('closeTabButton'+i).style.backgroundColor = "#bb0000" : document.getElementById('closeTabButton'+i).style.backgroundColor = "rgba(255,255,255,0.3)";
ICEcoder.changedContent[i-1]==1 ? document.getElementById('closeTabButton'+i).style.backgroundColor = "#b00" : document.getElementById('closeTabButton'+i).style.backgroundColor = "rgba(255,255,255,0.3)";
}
i==selectedTab ? tColor = "#ffffff" : tColor = "#000000";
i==selectedTab ? tColor = "#000" : tColor = "#fff";
document.getElementById('tab'+i).style.color = tColor;
i==selectedTab ? bgVPos = -22 : bgVPos = 0;
document.getElementById('tab'+i).style.backgroundPosition = "0px "+bgVPos+"px";
@@ -446,11 +450,11 @@ var ICEcoder = {
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, Ruby or CSS file
if (ICEcoder.htmlTagArray.length==0||fileName.indexOf(".js")>0||fileName.indexOf(".rb")>0||fileName.indexOf(".css")>0) {
ICEcoder.nestValid.style.backgroundColor="#00bb00";
if (ICEcoder.htmlTagArray.length==0||fileName.indexOf(".js")>0||fileName.indexOf(".coffee")>0||fileName.indexOf(".rb")>0||fileName.indexOf(".css")>0) {
ICEcoder.nestValid.style.backgroundColor="#0b0";
ICEcoder.nestValid.innerHTML = "Nesting OK";
} else {
ICEcoder.nestValid.style.backgroundColor="#ff0000";
ICEcoder.nestValid.style.backgroundColor="#f00";
ICEcoder.nestValid.innerHTML = "Nesting Broken";
}
},
@@ -496,13 +500,14 @@ var ICEcoder = {
fileName = ICEcoder.openFiles[ICEcoder.selectedTab-1];
if (fileName.indexOf(".js")>0) {caretLocType="JavaScript"};
if (fileName.indexOf(".coffee")>0) {caretLocType="CoffeeScript"};
if (fileName.indexOf(".rb")>0) {caretLocType="Ruby"};
if (fileName.indexOf(".css")>0) {caretLocType="CSS"};
ICEcoder.caretLocType = caretLocType;
// If we're in a JS, PHP or Ruby code block, add that to the nest display
if (caretLocType=="JavaScript"||caretLocType=="PHP"||caretLocType=="Ruby") {
// If we're in a JS, CoffeeScript PHP or Ruby code block, add that to the nest display
if (caretLocType=="JavaScript"||caretLocType=="CoffeeScript"||caretLocType=="PHP"||caretLocType=="Ruby") {
ICEcoder.nestDisplay.innerHTML += " &gt; " + caretLocType;
}
},
@@ -691,8 +696,8 @@ var ICEcoder = {
// Select or deselect file
selectDeselectFile: function(action,file) {
action == "select" ? file.style.backgroundColor="#888888" : file.style.backgroundColor="transparent";
action == "select" ? file.style.color="#ffffff" : file.style.color="#eeeeee";
action == "select" ? file.style.backgroundColor="#888" : file.style.backgroundColor="transparent";
action == "select" ? file.style.color="#fff" : file.style.color="#eee";
},
// Create a new file (start & instant save)
@@ -976,6 +981,8 @@ var ICEcoder = {
if (fileName) {
if (fileName.indexOf('.js')>0) {
cM.setOption("mode","javascript");
} else if (fileName.indexOf('.coffee')>0) {
cM.setOption("mode","coffeescript");
} else if (fileName.indexOf('.rb')>0) {
cM.setOption("mode","ruby");
} else if (fileName.indexOf('.css')>0) {
@@ -1037,7 +1044,7 @@ var ICEcoder = {
lCLen = lineContent.length;
adjustCursor = 3;
if (ICEcoder.caretLocType=="JavaScript"||ICEcoder.caretLocType=="PHP"||ICEcoder.caretLocType=="Ruby"||ICEcoder.caretLocType=="CSS") {
if (ICEcoder.caretLocType=="JavaScript"||ICEcoder.caretLocType=="CoffeeScript"||ICEcoder.caretLocType=="PHP"||ICEcoder.caretLocType=="Ruby"||ICEcoder.caretLocType=="CSS") {
if (cM.somethingSelected()) {
if (ICEcoder.caretLocType=="Ruby") {
startLine = cM.getCursor(true).line;
@@ -1053,7 +1060,7 @@ var ICEcoder = {
}
}
} else {
if (ICEcoder.caretLocType=="CSS") {
if (ICEcoder.caretLocType=="CSS"||ICEcoder.caretLocType=="CoffeeScript") {
lineContent.slice(0,3)!="/* " ? cM.setLine(linePos, "/* " + lineContent + " */") : cM.setLine(linePos, lineContent.slice(3,lCLen).slice(0,lCLen-5));
if (lineContent.slice(0,3)=="/* ") {adjustCursor = -adjustCursor};
} else if (ICEcoder.caretLocType=="Ruby") {
@@ -1322,7 +1329,7 @@ var ICEcoder = {
ICEcoder.serverQueueItems.splice(1,ICEcoder.serverQueueItems.length);
}
top.document.getElementById('loadingMask').style.visibility = "hidden";
top.ICEcoder.serverMessage('<b style="color: #dd0000">Cancelled tasks</b>');
top.ICEcoder.serverMessage('<b style="color: #d00">Cancelled tasks</b>');
setTimeout(function() {top.ICEcoder.serverMessage();},2000);
},

View File

@@ -157,7 +157,31 @@ if ($_GET['action']=="save") {
}
echo '<script>top.ICEcoder.serverMessage();top.ICEcoder.serverQueue("del",0);if (top.ICEcoder.stickyTabWindow.location) {top.ICEcoder.stickyTabWindow.location.reload()};action="doneSave";</script>';
} else {
echo "<script>alert('Sorry, this file has changed, cannot save\\n".$file."');</script>";
$loadedFile = file_get_contents($saveFile);
echo '<textarea name="loadedFile" id="loadedFile">'.str_replace("</textarea>","<ICEcoder:/:textarea>",$loadedFile).'</textarea>';
echo '<textarea name="userVersionFile" id="userVersionFile"></textarea>';
?>
<script>
var refreshFile = confirm('Sorry, this file has changed, cannot save\n<?php echo $file;?>\n\nReload this file and copy your version to a new document?');
if (refreshFile) {
var cM = top.ICEcoder.getcMInstance();
var thisTab = top.ICEcoder.selectedTab;
document.getElementById('userVersionFile').value = cM.getValue();
// Revert back to original
cM.setValue(document.getElementById('loadedFile').value);
top.ICEcoder.changedContent[thisTab-1] = 0;
top.ICEcoder.openFileMDTs[top.ICEcoder.selectedTab-1] = "<?php echo filemtime($saveFile); ?>";
cM.clearHistory();
// Now for the new file
top.ICEcoder.newTab();
cM = top.ICEcoder.getcMInstance();
cM.setValue(document.getElementById('userVersionFile').value);
cM.clearHistory();
// Finally, switch back to original tab
top.ICEcoder.switchTab(thisTab);
}
</script>
<?php
echo '<script>top.ICEcoder.serverMessage();top.ICEcoder.serverQueue("del",0);action="nothing";</script>';
}
} else {
@@ -206,7 +230,7 @@ if (action=="load") {
if (fileType=="image") {
top.document.getElementById('blackMask').style.visibility = "visible";
top.document.getElementById('mediaContainer').innerHTML = "<img src=\"<?php echo str_replace($docRoot,"",$file);?>\" class=\"whiteGlow\" style=\"border: solid 10px #ffffff; max-width: 700px; max-height: 500px\" onClick=\"return false\"><br><span class=\"whiteGlow\" style=\"border: solid 10px #ffffff; color: #000000; background-color: #ffffff\" onClick=\"return false\"><?php echo str_replace($docRoot,"",$file);?></span>";
top.document.getElementById('mediaContainer').innerHTML = "<img src=\"<?php echo str_replace($docRoot,"",$file);?>\" class=\"whiteGlow\" style=\"border: solid 10px #fff; max-width: 700px; max-height: 500px\" onClick=\"return false\"><br><span class=\"whiteGlow\" style=\"border: solid 10px #fff; color: #000; background-color: #fff\" onClick=\"return false\"><?php echo str_replace($docRoot,"",$file);?></span>";
}
top.ICEcoder.serverMessage();

View File

@@ -30,7 +30,7 @@ body {margin: 0px; overflow: auto}
}
.fileManager span {font-family: helvetica, arial, swiss, verdana;}
.fileManager a {color: #eeeeee; text-decoration: none;}
.fileManager a {color: #eee; text-decoration: none;}
.fileManager .open {font-style: italic;}
.fileManager .closed {font-style: normal;}
.fileManager .pft-directory {list-style-image: url(../images/file-manager-icons/directory.png);}
@@ -40,6 +40,7 @@ body {margin: 0px; overflow: auto}
.fileManager LI.pft-file { list-style-image: url(../images/file-manager-icons/file.png); }
/* Additional file types */
.fileManager LI.ext-coffee { list-style-image: url(../images/file-manager-icons/coffee.png); }
.fileManager LI.ext-css { list-style-image: url(../images/file-manager-icons/css.png); }
.fileManager LI.ext-doc { list-style-image: url(../images/file-manager-icons/doc.png); }
.fileManager LI.ext-gif { list-style-image: url(../images/file-manager-icons/gif.png); }

View File

@@ -23,9 +23,9 @@ body {overflow: hidden;
user-select: none;
}
h1 {font-size: 36px; font-weight: normal; color: #888888; margin-bottom: 20px}
h1 {font-size: 36px; font-weight: normal; color: #888; margin-bottom: 20px}
.help {font-family: arial, verdana, helvetica, sans-serif; background-color: #1c1c19; color: #ffffff; padding: 20px}
.help {font-family: arial, verdana, helvetica, sans-serif; background-color: #1c1c19; color: #fff; padding: 20px}
.help .key {display: inline-block; width: 175px; text-align: right; margin-right: 5px; float: left}
.help .key .plus {color: #888888}
.help .shortcut {display: inline-block; width: 175px; color: #888888; margin-left: 5px; float: left}
.help .key .plus {color: #888}
.help .shortcut {display: inline-block; width: 175px; color: #888; margin-left: 5px; float: left}

View File

@@ -23,11 +23,11 @@ body {overflow: hidden;
user-select: none;
}
h1 {font-size: 36px; font-weight: normal; color: #888888; margin-bottom: 20px}
h2 {font-size: 18px; font-weight: normal; color: #ffffff}
hr {border: 0; height: 1px; background-color: #888888}
h1 {font-size: 36px; font-weight: normal; color: #888; margin-bottom: 20px}
h2 {font-size: 18px; font-weight: normal; color: #fff}
hr {border: 0; height: 1px; background-color: #888}
.results {font-family: arial, verdana, helvetica, sans-serif; background-color: #1c1c19; color: #ffffff}
.results {font-family: arial, verdana, helvetica, sans-serif; background-color: #1c1c19; color: #fff}
.results .resultsPane {position: relative; width: 660px; height: 560px; font-size: 10px; padding: 20px; float: left}
.results .resultsPane a {color: rgba(0,198,255,0.7); text-decoration: none}
.results .resultsPane a:hover {text-decoration: underline}

View File

@@ -23,18 +23,18 @@ body {overflow: hidden;
user-select: none;
}
h1 {font-size: 36px; font-weight: normal; color: #888888; margin-bottom: 20px}
h2 {font-size: 18px; font-weight: normal; color: #ffffff}
h1 {font-size: 36px; font-weight: normal; color: #888; margin-bottom: 20px}
h2 {font-size: 18px; font-weight: normal; color: #fff}
.settings {font-family: arial, verdana, helvetica, sans-serif; background-color: #1c1c19; color: #ffffff}
.settings .infoPane {position: relative; width: 230px; height: 560px; font-size: 10px; color: #bbbbbb; background-color: #ffffff; padding: 20px; float: left}
.settings .infoPane a {color: #888888; text-decoration: none}
.settings {font-family: arial, verdana, helvetica, sans-serif; background-color: #1c1c19; color: #fff}
.settings .infoPane {position: relative; width: 230px; height: 560px; font-size: 10px; color: #bbb; background-color: #fff; padding: 20px; float: left}
.settings .infoPane a {color: #888; text-decoration: none}
.settings .infoPane a:hover {text-decoration: underline}
.settings .logo {position: relative; margin-top: 2px}
.settings .version {position: relative; display: block; margin-top: 5px; margin-bottom: 10px; font-size: 10px; color: #bbbbbb}
.settings .version {position: relative; display: block; margin-top: 5px; margin-bottom: 10px; font-size: 10px; color: #bbb}
.settings .settingsColumn1 {width: 220px; height: 560px; padding: 20px; float: left}
.settings .settingsColumn2 {width: 420px; height: 560px; padding: 20px 20px 20px 0px; float: left}
.settings input, .settings textarea {border: 1px solid #555555; background-color: #444444; color: #ffffff}
.settings input, .settings textarea {border: 1px solid #555; background-color: #444; color: #fff}
.settings input:focus, .settings textarea:focus {
outline: none;
-webkit-box-shadow: 0px 0px 10px 1px rgba(0,198,255,0.7);

View File

@@ -12,15 +12,15 @@
<script src="../<?php echo $codeMirrorDir; ?>/mode/javascript/javascript.js"></script>
<style type="text/css">
.CodeMirror {position: absolute; width: 0px; background-color: #ffffff; font-family: monospace}
.CodeMirror {position: absolute; width: 0px; background-color: #fff; font-family: monospace}
.CodeMirror-scroll {height: 220px; width: 420px; overflow: hidden}
.cm-s-visible {display: block; top: 0px}
.cm-s-hidden {display: none; top: 4000px}
.cm-s-activeLine {background: #002 !important;}
// Make sure this next one remains the 5th item, updated with JS
.cm-tab:after {position: relative; display: inline-block; width: 0px; left: -1.4em; overflow: visible; color: #aaa; content: "<?php if($visibleTabs) {echo '\21e5';};?>";}
span.CodeMirror-matchhighlight {background: #555555}
.CodeMirror-focused span.CodeMirror-matchhighlight {color: #000000; background: #555555; !important}
span.CodeMirror-matchhighlight {background: #555}
.CodeMirror-focused span.CodeMirror-matchhighlight {color: #000; background: #555; !important}
</style>
<link rel="stylesheet" href="editor.css">
@@ -90,7 +90,7 @@ for ($i=0;$i<count($themeArray)-1;$i++) {
<br>
<h2>security</h2>
new password <span style="font-size: 10px; color: #888888">8 chars</span><br>
new password <span style="font-size: 10px; color: #888">8 chars</span><br>
<input type="password" name="accountPassword" onkeydown="showButton()"><br>
confirm password<br>
<input type="password" name="confirmPassword" onkeydown="showButton()"><br>
@@ -108,7 +108,7 @@ ip addresses<br>
<div class="settingsColumn2">
<h2>plugins</h2>
plugins array <span style="font-size: 10px; color: #888888">name, img src, style, url, target, setInterval (mins)</span><br>
plugins array <span style="font-size: 10px; color: #888">name, img src, style, url, target, setInterval (mins)</span><br>
<textarea name="plugins" class="plugins" onkeydown="showButton()"><?php
for($i=0;$i<count($plugins);$i++) {
for($j=0;$j<count($plugins[$i]);$j++) {

View File

@@ -1,261 +1,261 @@
<?php
session_start();
// Function to handle salted hashing
define('SALT_LENGTH',9);
function generateHash($plainText,$salt=null) {
if ($salt === null) {
$salt = substr(md5(uniqid(rand(), true)),0,SALT_LENGTH);
} else {
$salt = substr($salt,0,SALT_LENGTH);
}
return $salt.sha1($salt.$plainText);
}
// -----------------
// Start of settings
// -----------------
$versionNo = "v 0.6.7";
$codeMirrorDir = "CodeMirror-2.24";
$cMThisVer = 2.24;
$tabsIndent = true;
$testcMVersion = false;
$openLastFiles = true;
$codeAssist = true;
$visibleTabs = false;
$lockedNav = true;
$accountPassword = "";
$restrictedFiles = array("wp-",".php",".rb",".sql");
$bannedFiles = array("_coder","wp-",".exe");
$allowedIPs = array("*");
$plugins = array(
array("Database Admin","images/database.png","margin-top: 3px","plugins/adminer/adminer-3.3.3-mysql-en.php","_blank",""),
array("Batch Image Processor","images/images.png","margin-top: 5px","http://birme.net","_blank",""),
array("Backup","images/backup-open-files.png","margin-top: 3px","plugins/backupOpenFiles/index.php","fileControl:<b>Zipping Open Files</b>","10")
);
$theme = "default";
$lastOpenedFiles = "";
// ---------------
// End of settings
// ---------------
// Update this settings file?
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
$settingsFile = 'settings.php';
$settingsContents = file_get_contents($settingsFile);
// Replace our lastOpenedFiles var with the the current
$repPosStart = strpos($settingsContents,'$tabsIndent');
$repPosEnd = strpos($settingsContents,'$lastOpenedFiles');
// Prepare all our vars
if ($_POST['tabsIndent']) {$tabsIndent = "true";} else {$tabsIndent = "false";};
if ($_POST['testcMVersion']) {$testcMVersion = "true";} else {$testcMVersion = "false";};
if ($_POST['openLastFiles']) {$openLastFiles = "true";} else {$openLastFiles = "false";};
if ($_POST['codeAssist']) {$codeAssist = "true";} else {$codeAssist = "false";};
if ($_POST['visibleTabs']) {$visibleTabs = "true";} else {$visibleTabs = "false";};
if ($_POST['lockedNav']) {$lockedNav = "true";} else {$lockedNav = "false";};
if ($_POST['accountPassword']!="") {$accountPassword = generateHash($_POST['accountPassword']);} else {$accountPassword = $_POST['oldPassword'];};
$restrictedFiles = 'array("'.str_replace(', ','","',$_POST['restrictedFiles']).'")';
$bannedFiles = 'array("'.str_replace(', ','","',$_POST['bannedFiles']).'")';
$allowedIPs = 'array("'.str_replace(', ','","',$_POST['allowedIPs']).'")';
$plugins = 'array('.PHP_EOL.' array('.PHP_EOL.' '.str_replace('====================','),'.PHP_EOL.' array(',$_POST['plugins']).'))';
$theme = $_POST['theme'];
$settingsNew = '$tabsIndent = '.$tabsIndent.';'.PHP_EOL;
$settingsNew .= '$testcMVersion = '.$testcMVersion.';'.PHP_EOL;
$settingsNew .= '$openLastFiles = '.$openLastFiles.';'.PHP_EOL;
$settingsNew .= '$codeAssist = '.$codeAssist.';'.PHP_EOL;
$settingsNew .= '$visibleTabs = '.$visibleTabs.';'.PHP_EOL;
$settingsNew .= '$lockedNav = '.$lockedNav.';'.PHP_EOL;
$settingsNew .= '$accountPassword = "'.$accountPassword.'";'.PHP_EOL;
$settingsNew .= '$restrictedFiles = '.$restrictedFiles.';'.PHP_EOL;
$settingsNew .= '$bannedFiles = '.$bannedFiles.';'.PHP_EOL;
$settingsNew .= '$allowedIPs = '.$allowedIPs.';'.PHP_EOL;
$settingsNew .= '$plugins = '.$plugins.';'.PHP_EOL;
$settingsNew .= '$theme = "'.$theme.'";'.PHP_EOL;
// Compile our new settings
$settingsContents = substr($settingsContents,0,$repPosStart).$settingsNew.substr($settingsContents,($repPosEnd),strlen($settingsContents));
// Now update this file
$fh = fopen($settingsFile, 'w') or die("can't update settings file");
fwrite($fh, $settingsContents);
fclose($fh);
// OK, now this file is updated, update our current session with new arrays
$_SESSION['restrictedFiles'] = $restrictedFiles = explode(", ",$_POST['restrictedFiles']);
$_SESSION['bannedFiles'] = $bannedFiles = explode(", ",$_POST['bannedFiles']);
$_SESSION['allowedIPs'] = $allowedIPs = explode(", ",$_POST['allowedIPs']);
// Work out the theme to use now
if ($theme=="default") {$themeURL="lib/editor.css";} else {$themeURL=$codeMirrorDir."/theme/".$theme.".css";};
// Do we need a file manager refresh?
if ($_POST['changedFileSettings']=="true") {$refreshFM="true";} else {$refreshFM="false";};
// With all that worked out, we can now hide the settings screen and apply the new settings
echo "<script>top.ICEcoder.settingsScreen('hide');top.ICEcoder.useNewSettings('".$themeURL."',".$tabsIndent.",".$codeAssist.",".$lockedNav.",".$visibleTabs.",".$refreshFM.");</script>";
}
// Save the currently opened files for next time
if (isset($_GET["saveFiles"]) && $_GET['saveFiles']) {
if ($_SESSION['userLevel'] == 10) {
$settingsFile = 'settings.php';
$settingsContents = file_get_contents($settingsFile);
// Replace our lastOpenedFiles var with the the current
$repPosStart = strpos($settingsContents,'lastOpenedFiles = "')+19;
$repPosEnd = strpos($settingsContents,'";',$repPosStart)-$repPosStart;
$settingsContents = substr($settingsContents,0,$repPosStart).$_GET['saveFiles'].substr($settingsContents,($repPosStart+$repPosEnd),strlen($settingsContents));
// Now update this file
$fh = fopen($settingsFile, 'w') or die("can't update settings file");
fwrite($fh, $settingsContents);
fclose($fh);
}
echo '<script>top.ICEcoder.serverMessage();top.ICEcoder.serverQueue("del",0);</script>';
}
// Establish our user level
if (!isset($_SESSION['userLevel'])) {$_SESSION['userLevel'] = 0;};
if(isset($_POST['loginPassword']) && generateHash($_POST['loginPassword'],$accountPassword)==$accountPassword) {$_SESSION['userLevel'] = 10;};
$_SESSION['userLevel'] = $_SESSION['userLevel'];
if (!isset($_SESSION['restrictedFiles'])) {$_SESSION['restrictedFiles'] = $restrictedFiles;}
if (!isset($_SESSION['bannedFiles'])) {$_SESSION['bannedFiles'] = $bannedFiles;}
if (!isset($_SESSION['allowedIPs'])) {$_SESSION['allowedIPs'] = $allowedIPs;}
// Determin our allowed IP addresses
$allowedIP = false;
for($i=0;$i<count($_SESSION['allowedIPs']);$i++) {
if ($_SESSION['allowedIPs'][$i]==$_SERVER["REMOTE_ADDR"]||$_SESSION['allowedIPs'][$i]=="*") {
$allowedIP = true;
}
}
// If user not allowed to view, boot to site root
if (!$allowedIP) {
echo '<script>top.window.location="/";</script>';
};
// Establish our shortened URL, explode the path based on server type (Linux or Windows)
if (strpos($_SERVER['DOCUMENT_ROOT'],"/")>-1) {$slashType = "/";} else {$slashType = "\\";};
$shortURLStarts = explode($slashType,$_SERVER['DOCUMENT_ROOT']);
// Then clear item at the end if there is one, plus trailing slash
// We end up with the directory name of the server root
if ($shortURLStarts[count($shortURLStarts)-1]!="") {$trimArray=1;} else {$trimArray=2;}
$shortURLStarts = $shortURLStarts[count($shortURLStarts)-$trimArray];
// If we're updating or calling from the index.php page, do/redo plugins & last opened files
if ((isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) || strpos($_SERVER['PHP_SELF'],"index.php")>0) {
// If we're updating, we need to recreate the plugins array
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
$plugins = array();
$pluginsArray = explode("====================",str_replace("\"","",str_replace("\r","",str_replace("\n","",$_POST['plugins']))));
for ($i=0;$i<count($pluginsArray);$i++) {
array_push($plugins, explode(",",$pluginsArray[$i]));
}
}
// Work out the plugins to display to the user
$pluginsDisplay = "";
for ($i=0;$i<count($plugins);$i++) {
$target = explode(":",$plugins[$i][4]);
$pluginsDisplay .= '<a href="'.$plugins[$i][3].'" target="'.$target[0].'"><img src="'.$plugins[$i][1].'" style="'.$plugins[$i][2].'" alt="'.$plugins[$i][0].'"></a>';
};
// If we're updating, replace the plugin display with our newly established one
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
echo "<script>top.document.getElementById('pluginsContainer').innerHTML = '".$pluginsDisplay."';</script>";
}
// Work out what plugins we'll need to set on a setInterval
$onLoadExtras = "";
for ($i=0;$i<count($plugins);$i++) {
if ($plugins[$i][5]!="") {
$onLoadExtras .= ";top.ICEcoder.startPluginIntervals(".$i.",'".$plugins[$i][3]."','".$plugins[$i][4]."','".$plugins[$i][5]."')";
};
};
// If we're updating our settings, clear existing setIntervals & the array refs, then start new ones
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
?>
<script>
for (i=0;i<=top.ICEcoder.pluginIntervalRefs.length-1;i++) {
clearInterval(top.ICEcoder['plugTimer'+top.ICEcoder.pluginIntervalRefs[i]]);
}
top.ICEcoder.pluginIntervalRefs = [];
<?php echo $onLoadExtras.PHP_EOL; ?>
</script>
<?php
}
// Finally, open last opened files if we need to (applies to index.php only)
if ($openLastFiles) {
$onLoadExtras .= ";top.ICEcoder.autoOpenFiles()";
}
}
// If we're due to show the settings screen
if ($accountPassword == "" && isset($_GET['settings'])) {
?>
<!DOCTYPE html>
<html>
<head>
<title>ICE Coder - <?php echo $versionNo;?> :: Settings</title>
<link rel="stylesheet" type="text/css" href="coder.css">
</head>
<body style="background-color: #ffffff">
<div class="screenContainer">
<div class="screenVCenter">
<div class="screenCenter">
<img src="../images/ice-coder.gif">
<div class="version"><?php echo $versionNo;?></div>
<form name="settingsUpdate" action="../index.php" method="POST">
<input type="password" name="accountPassword" class="accountPassword">
<input type="submit" name="submit" value="Set Password" class="button">
</form>
</div>
</div>
</div>
</body>
</html>
<?php
} else {
// If the password hasn't been set, set it, but only if we're including
// from the index.php file (as this file is included from multiple places)
if ($accountPassword == "" && strpos($_SERVER['PHP_SELF'],"index.php")>0) {
// If we're setting a password
if (isset($_POST['accountPassword'])) {
$password = generateHash($_POST['accountPassword']);
$settingsFile = 'lib/settings.php';
$settingsContents = file_get_contents($settingsFile);
// Replace our empty password with the one submitted by user
$settingsContents = str_replace('$accountPassword = "";','$accountPassword = "'.$password.'";',$settingsContents);
// Now update this file
$fh = fopen($settingsFile, 'w') or die("can't update settings file");
fwrite($fh, $settingsContents);
fclose($fh);
// Set the session user level
$_SESSION['userLevel'] = 10;
// Finally, load again as now this file has changed and auto login
header('Location: index.php');
} else {
// We need to set the password
header('Location: lib/settings.php?settings=set');
}
}
// If we're logging in, refresh the file manager and show icons if login is correct
if(isset($_POST['loginPassword'])) {
if(isset($_POST['loginPassword']) && generateHash($_POST['loginPassword'],$accountPassword)==$accountPassword) {
$loginAttempt = 'loginOK';
} else {
$loginAttempt = 'loginFailed';
}
echo "<script>top.ICEcoder.refreshFileManager('".$loginAttempt."');</script>";
}
}
<?php
session_start();
// Function to handle salted hashing
define('SALT_LENGTH',9);
function generateHash($plainText,$salt=null) {
if ($salt === null) {
$salt = substr(md5(uniqid(rand(), true)),0,SALT_LENGTH);
} else {
$salt = substr($salt,0,SALT_LENGTH);
}
return $salt.sha1($salt.$plainText);
}
// -----------------
// Start of settings
// -----------------
$versionNo = "v 0.6.8";
$codeMirrorDir = "CodeMirror-2.25";
$cMThisVer = 2.25;
$tabsIndent = true;
$testcMVersion = false;
$openLastFiles = true;
$codeAssist = true;
$visibleTabs = false;
$lockedNav = true;
$accountPassword = "";
$restrictedFiles = array("wp-",".php",".rb",".sql");
$bannedFiles = array("_coder","wp-",".exe");
$allowedIPs = array("*");
$plugins = array(
array("Database Admin","images/database.png","margin-top: 3px","plugins/adminer/adminer-3.3.3-mysql-en.php","_blank",""),
array("Batch Image Processor","images/images.png","margin-top: 5px","http://birme.net","_blank",""),
array("Backup","images/backup-open-files.png","margin-top: 3px","plugins/backupOpenFiles/index.php","fileControl:<b>Zipping Open Files</b>","10")
);
$theme = "default";
$lastOpenedFiles = "";
// ---------------
// End of settings
// ---------------
// Update this settings file?
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
$settingsFile = 'settings.php';
$settingsContents = file_get_contents($settingsFile);
// Replace our lastOpenedFiles var with the the current
$repPosStart = strpos($settingsContents,'$tabsIndent');
$repPosEnd = strpos($settingsContents,'$lastOpenedFiles');
// Prepare all our vars
if ($_POST['tabsIndent']) {$tabsIndent = "true";} else {$tabsIndent = "false";};
if ($_POST['testcMVersion']) {$testcMVersion = "true";} else {$testcMVersion = "false";};
if ($_POST['openLastFiles']) {$openLastFiles = "true";} else {$openLastFiles = "false";};
if ($_POST['codeAssist']) {$codeAssist = "true";} else {$codeAssist = "false";};
if ($_POST['visibleTabs']) {$visibleTabs = "true";} else {$visibleTabs = "false";};
if ($_POST['lockedNav']) {$lockedNav = "true";} else {$lockedNav = "false";};
if ($_POST['accountPassword']!="") {$accountPassword = generateHash($_POST['accountPassword']);} else {$accountPassword = $_POST['oldPassword'];};
$restrictedFiles = 'array("'.str_replace(', ','","',$_POST['restrictedFiles']).'")';
$bannedFiles = 'array("'.str_replace(', ','","',$_POST['bannedFiles']).'")';
$allowedIPs = 'array("'.str_replace(', ','","',$_POST['allowedIPs']).'")';
$plugins = 'array('.PHP_EOL.' array('.PHP_EOL.' '.str_replace('====================','),'.PHP_EOL.' array(',$_POST['plugins']).'))';
$theme = $_POST['theme'];
$settingsNew = '$tabsIndent = '.$tabsIndent.';'.PHP_EOL;
$settingsNew .= '$testcMVersion = '.$testcMVersion.';'.PHP_EOL;
$settingsNew .= '$openLastFiles = '.$openLastFiles.';'.PHP_EOL;
$settingsNew .= '$codeAssist = '.$codeAssist.';'.PHP_EOL;
$settingsNew .= '$visibleTabs = '.$visibleTabs.';'.PHP_EOL;
$settingsNew .= '$lockedNav = '.$lockedNav.';'.PHP_EOL;
$settingsNew .= '$accountPassword = "'.$accountPassword.'";'.PHP_EOL;
$settingsNew .= '$restrictedFiles = '.$restrictedFiles.';'.PHP_EOL;
$settingsNew .= '$bannedFiles = '.$bannedFiles.';'.PHP_EOL;
$settingsNew .= '$allowedIPs = '.$allowedIPs.';'.PHP_EOL;
$settingsNew .= '$plugins = '.$plugins.';'.PHP_EOL;
$settingsNew .= '$theme = "'.$theme.'";'.PHP_EOL;
// Compile our new settings
$settingsContents = substr($settingsContents,0,$repPosStart).$settingsNew.substr($settingsContents,($repPosEnd),strlen($settingsContents));
// Now update this file
$fh = fopen($settingsFile, 'w') or die("can't update settings file");
fwrite($fh, $settingsContents);
fclose($fh);
// OK, now this file is updated, update our current session with new arrays
$_SESSION['restrictedFiles'] = $restrictedFiles = explode(", ",$_POST['restrictedFiles']);
$_SESSION['bannedFiles'] = $bannedFiles = explode(", ",$_POST['bannedFiles']);
$_SESSION['allowedIPs'] = $allowedIPs = explode(", ",$_POST['allowedIPs']);
// Work out the theme to use now
if ($theme=="default") {$themeURL="lib/editor.css";} else {$themeURL=$codeMirrorDir."/theme/".$theme.".css";};
// Do we need a file manager refresh?
if ($_POST['changedFileSettings']=="true") {$refreshFM="true";} else {$refreshFM="false";};
// With all that worked out, we can now hide the settings screen and apply the new settings
echo "<script>top.ICEcoder.settingsScreen('hide');top.ICEcoder.useNewSettings('".$themeURL."',".$tabsIndent.",".$codeAssist.",".$lockedNav.",".$visibleTabs.",".$refreshFM.");</script>";
}
// Save the currently opened files for next time
if (isset($_GET["saveFiles"]) && $_GET['saveFiles']) {
if ($_SESSION['userLevel'] == 10) {
$settingsFile = 'settings.php';
$settingsContents = file_get_contents($settingsFile);
// Replace our lastOpenedFiles var with the the current
$repPosStart = strpos($settingsContents,'lastOpenedFiles = "')+19;
$repPosEnd = strpos($settingsContents,'";',$repPosStart)-$repPosStart;
$settingsContents = substr($settingsContents,0,$repPosStart).$_GET['saveFiles'].substr($settingsContents,($repPosStart+$repPosEnd),strlen($settingsContents));
// Now update this file
$fh = fopen($settingsFile, 'w') or die("can't update settings file");
fwrite($fh, $settingsContents);
fclose($fh);
}
echo '<script>top.ICEcoder.serverMessage();top.ICEcoder.serverQueue("del",0);</script>';
}
// Establish our user level
if (!isset($_SESSION['userLevel'])) {$_SESSION['userLevel'] = 0;};
if(isset($_POST['loginPassword']) && generateHash($_POST['loginPassword'],$accountPassword)==$accountPassword) {$_SESSION['userLevel'] = 10;};
$_SESSION['userLevel'] = $_SESSION['userLevel'];
if (!isset($_SESSION['restrictedFiles'])) {$_SESSION['restrictedFiles'] = $restrictedFiles;}
if (!isset($_SESSION['bannedFiles'])) {$_SESSION['bannedFiles'] = $bannedFiles;}
if (!isset($_SESSION['allowedIPs'])) {$_SESSION['allowedIPs'] = $allowedIPs;}
// Determin our allowed IP addresses
$allowedIP = false;
for($i=0;$i<count($_SESSION['allowedIPs']);$i++) {
if ($_SESSION['allowedIPs'][$i]==$_SERVER["REMOTE_ADDR"]||$_SESSION['allowedIPs'][$i]=="*") {
$allowedIP = true;
}
}
// If user not allowed to view, boot to site root
if (!$allowedIP) {
echo '<script>top.window.location="/";</script>';
};
// Establish our shortened URL, explode the path based on server type (Linux or Windows)
if (strpos($_SERVER['DOCUMENT_ROOT'],"/")>-1) {$slashType = "/";} else {$slashType = "\\";};
$shortURLStarts = explode($slashType,$_SERVER['DOCUMENT_ROOT']);
// Then clear item at the end if there is one, plus trailing slash
// We end up with the directory name of the server root
if ($shortURLStarts[count($shortURLStarts)-1]!="") {$trimArray=1;} else {$trimArray=2;}
$shortURLStarts = $shortURLStarts[count($shortURLStarts)-$trimArray];
// If we're updating or calling from the index.php page, do/redo plugins & last opened files
if ((isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) || strpos($_SERVER['PHP_SELF'],"index.php")>0) {
// If we're updating, we need to recreate the plugins array
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
$plugins = array();
$pluginsArray = explode("====================",str_replace("\"","",str_replace("\r","",str_replace("\n","",$_POST['plugins']))));
for ($i=0;$i<count($pluginsArray);$i++) {
array_push($plugins, explode(",",$pluginsArray[$i]));
}
}
// Work out the plugins to display to the user
$pluginsDisplay = "";
for ($i=0;$i<count($plugins);$i++) {
$target = explode(":",$plugins[$i][4]);
$pluginsDisplay .= '<a href="'.$plugins[$i][3].'" target="'.$target[0].'"><img src="'.$plugins[$i][1].'" style="'.$plugins[$i][2].'" alt="'.$plugins[$i][0].'"></a>';
};
// If we're updating, replace the plugin display with our newly established one
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
echo "<script>top.document.getElementById('pluginsContainer').innerHTML = '".$pluginsDisplay."';</script>";
}
// Work out what plugins we'll need to set on a setInterval
$onLoadExtras = "";
for ($i=0;$i<count($plugins);$i++) {
if ($plugins[$i][5]!="") {
$onLoadExtras .= ";top.ICEcoder.startPluginIntervals(".$i.",'".$plugins[$i][3]."','".$plugins[$i][4]."','".$plugins[$i][5]."')";
};
};
// If we're updating our settings, clear existing setIntervals & the array refs, then start new ones
if (isset($_POST["theme"]) && $_POST["theme"] && $_SESSION['userLevel'] == 10) {
?>
<script>
for (i=0;i<=top.ICEcoder.pluginIntervalRefs.length-1;i++) {
clearInterval(top.ICEcoder['plugTimer'+top.ICEcoder.pluginIntervalRefs[i]]);
}
top.ICEcoder.pluginIntervalRefs = [];
<?php echo $onLoadExtras.PHP_EOL; ?>
</script>
<?php
}
// Finally, open last opened files if we need to (applies to index.php only)
if ($openLastFiles) {
$onLoadExtras .= ";top.ICEcoder.autoOpenFiles()";
}
}
// If we're due to show the settings screen
if ($accountPassword == "" && isset($_GET['settings'])) {
?>
<!DOCTYPE html>
<html>
<head>
<title>ICE Coder - <?php echo $versionNo;?> :: Settings</title>
<link rel="stylesheet" type="text/css" href="coder.css">
</head>
<body style="background-color: #fff">
<div class="screenContainer">
<div class="screenVCenter">
<div class="screenCenter">
<img src="../images/ice-coder.gif">
<div class="version"><?php echo $versionNo;?></div>
<form name="settingsUpdate" action="../index.php" method="POST">
<input type="password" name="accountPassword" class="accountPassword">
<input type="submit" name="submit" value="Set Password" class="button">
</form>
</div>
</div>
</div>
</body>
</html>
<?php
} else {
// If the password hasn't been set, set it, but only if we're including
// from the index.php file (as this file is included from multiple places)
if ($accountPassword == "" && strpos($_SERVER['PHP_SELF'],"index.php")>0) {
// If we're setting a password
if (isset($_POST['accountPassword'])) {
$password = generateHash($_POST['accountPassword']);
$settingsFile = 'lib/settings.php';
$settingsContents = file_get_contents($settingsFile);
// Replace our empty password with the one submitted by user
$settingsContents = str_replace('$accountPassword = "";','$accountPassword = "'.$password.'";',$settingsContents);
// Now update this file
$fh = fopen($settingsFile, 'w') or die("can't update settings file");
fwrite($fh, $settingsContents);
fclose($fh);
// Set the session user level
$_SESSION['userLevel'] = 10;
// Finally, load again as now this file has changed and auto login
header('Location: index.php');
} else {
// We need to set the password
header('Location: lib/settings.php?settings=set');
}
}
// If we're logging in, refresh the file manager and show icons if login is correct
if(isset($_POST['loginPassword'])) {
if(isset($_POST['loginPassword']) && generateHash($_POST['loginPassword'],$accountPassword)==$accountPassword) {
$loginAttempt = 'loginOK';
} else {
$loginAttempt = 'loginFailed';
}
echo "<script>top.ICEcoder.refreshFileManager('".$loginAttempt."');</script>";
}
}
?>