diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/codemirror.js b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/codemirror.js
index 640f07ff1..390e68c1f 100644
--- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/codemirror.js
+++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/codemirror.js
@@ -20,15 +20,15 @@ var CodeMirror = (function() {
// This mess creates the base DOM structure for the editor.
wrapper.innerHTML =
'
' + // Set to the height of the text, causes scrolling
- '
' + // To measure line/char size
+ '' + // To measure line/char size
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' +
- '' + // Moved around its parent to cover visible view
+ '
' + // Moved around its parent to cover visible view
'
' +
'
' + // Wraps and hides input textarea
- '
' +
+ '
' +
// Provides positioning relative to (visible) text origin
'
' +
- '
' + // Absolutely positioned blinky cursor
+ '
' + // Absolutely positioned blinky cursor
'
'; // This DIV contains the actual code
if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
// I've never seen more elegant code in my life.
@@ -59,9 +59,9 @@ var CodeMirror = (function() {
var shiftSelecting, reducedSelection;
// Variables used by startOperation/endOperation to track what
// happened during the operation.
- var updateInput, changes, textChanged, selectionChanged;
+ var updateInput, changes, textChanged, selectionChanged, leaveInputAlone;
// Current visible range (may be bigger than the view window).
- var showingFrom = 0, showingTo = 0;
+ var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
// editing will hold an object describing the things we put in the
// textarea, to help figure out whether something changed.
// bracketHighlighted is used to remember that a backet has been
@@ -75,12 +75,16 @@ var CodeMirror = (function() {
// Register our event handlers.
connect(wrapper, "mousedown", operation(onMouseDown));
+ // Gecko browsers fire contextmenu *after* opening the menu, at
+ // which point we can't mess with it anymore. Context menu is
+ // handled in onMouseDown for Gecko.
+ if (!gecko) connect(wrapper, "contextmenu", operation(onContextMenu));
connect(code, "dblclick", operation(onDblClick));
- connect(wrapper, "scroll", function() {updateDisplay(); if (options.onScroll) options.onScroll(instance);});
- connect(window, "resize", updateDisplay);
+ connect(wrapper, "scroll", function() {updateDisplay([]); if (options.onScroll) options.onScroll(instance);});
+ connect(window, "resize", function() {updateDisplay(true);});
connect(input, "keyup", operation(onKeyUp));
connect(input, "keydown", operation(onKeyDown));
- connect(input, "keypress", onKeyPress);
+ connect(input, "keypress", operation(onKeyPress));
connect(input, "focus", onFocus);
connect(input, "blur", onBlur);
@@ -89,6 +93,7 @@ var CodeMirror = (function() {
connect(wrapper, "drop", operation(onDrop));
connect(wrapper, "paste", function(){input.focus(); fastPoll();});
connect(input, "paste", function(){fastPoll();});
+ connect(input, "cut", function(){fastPoll();});
if (document.activeElement == input) onFocus();
else onBlur();
@@ -125,15 +130,22 @@ var CodeMirror = (function() {
return pageCoords(start ? sel.from : sel.to);
},
charCoords: function(pos){return pageCoords(clipPos(pos));},
+ coordsChar: function(coords) {
+ var off = eltOffset(lineSpace);
+ var line = Math.min(showingTo - 1, showingFrom + Math.floor(coords.y / lineHeight()));
+ return clipPos({line: line, ch: charFromX(clipLine(line), coords.x)});
+ },
getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
markText: operation(function(a, b, c){return operation(markText(a, b, c));}),
setMarker: addGutterMarker,
clearMarker: removeGutterMarker,
+ setLineClass: operation(setLineClass),
lineInfo: lineInfo,
addWidget: function(pos, node, scroll) {
var pos = localCoords(clipPos(pos), true);
- node.style.top = pos.yBot + "px"; node.style.left = pos.x + "px";
- lineSpace.appendChild(node);
+ node.style.top = (showingFrom * lineHeight() + pos.yBot + paddingTop()) + "px";
+ node.style.left = (pos.x + paddingLeft()) + "px";
+ code.appendChild(node);
if (scroll)
scrollIntoView(pos.x, pos.yBot, pos.x + node.offsetWidth, pos.yBot + node.offsetHeight);
},
@@ -160,7 +172,7 @@ var CodeMirror = (function() {
getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
operation: function(f){return operation(f)();},
- refresh: function(){updateDisplay([{from: 0, to: lines.length}]);},
+ refresh: function(){updateDisplay(true);},
getInputField: function(){return input;},
getWrapperElement: function(){return wrapper;}
};
@@ -188,12 +200,13 @@ var CodeMirror = (function() {
return e.stop();
}
+ if (gecko && e.button() == 3) onContextMenu(e);
+ if (e.button() != 1) return;
// For button 1, if it was clicked inside the editor
// (posFromMouse returning non-null), we have to adjust the
// selection.
var start = posFromMouse(e), last = start, going;
if (!start) {if (e.target() == wrapper) e.stop(); return;}
- if (e.button() != 1) return; // TODO can we make copy/cut context menu work?
setCursor(start.line, start.ch, false);
if (!focused) onFocus();
@@ -221,7 +234,6 @@ var CodeMirror = (function() {
var move = connect(document, "mousemove", operation(function(e) {
clearTimeout(going);
e.stop();
- if (!e.button()) {end(); return;}
extend(e);
}), true);
var up = connect(document, "mouseup", operation(function(e) {
@@ -276,7 +288,7 @@ var CodeMirror = (function() {
if (mod && (code == 36 || code == 35)) {scrollEnd(code == 36); return e.stop();} // ctrl-home/end
if (mod && code == 65) {selectAll(); return e.stop();} // ctrl-a
if (!options.readOnly) {
- if (!anyMod && code == 13) {handleEnter(); return e.stop();} // enter
+ if (!anyMod && code == 13) {return;} // enter
if (!anyMod && code == 9 && handleTab(e.e.shiftKey)) return e.stop(); // tab
if (mod && code == 90) {undo(); return e.stop();} // ctrl-z
if (mod && ((e.e.shiftKey && code == 90) || code == 89)) {redo(); return e.stop();} // ctrl-shift-z, ctrl-y
@@ -288,15 +300,15 @@ var CodeMirror = (function() {
// its start when it is inverted and a movement key is pressed
// (and later restore it again), shouldn't be used for
// non-movement keys.
- var id = (mod ? "c" : "") + code;
- if (sel.inverted && movementKeys.hasOwnProperty(id)) {
+ curKeyId = (mod ? "c" : "") + code;
+ if (sel.inverted && movementKeys.hasOwnProperty(curKeyId)) {
var range = selRange(input);
if (range) {
reducedSelection = {anchor: range.start};
setSelRange(input, range.start, range.start);
}
}
- fastPoll(id);
+ fastPoll(curKeyId);
}
function onKeyUp(e) {
if (reducedSelection) {
@@ -314,7 +326,9 @@ var CodeMirror = (function() {
}
var code = e.e.keyCode;
// Re-stop tab and enter. Necessary on some browsers.
- if (code == 13 || (code == 9 && options.tabMode != "default")) e.stop();
+ if (code == 13) {handleEnter(); e.stop();}
+ else if (code == 9 && options.tabMode != "default") e.stop();
+ else fastPoll(curKeyId);
}
function onFocus() {
@@ -453,7 +467,9 @@ var CodeMirror = (function() {
return getRange(sel.from, sel.to);
}
+ var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
function slowPoll() {
+ if (pollingFast) return;
poll.set(2000, function() {
startOperation();
readInput();
@@ -462,14 +478,14 @@ var CodeMirror = (function() {
});
}
function fastPoll(keyId) {
- var misses = 0;
+ var missed = false;
+ pollingFast = true;
function p() {
startOperation();
var changed = readInput();
if (changed == "moved" && keyId) movementKeys[keyId] = true;
- if (changed) {poll.set(80, p); misses = 0;}
- else if (misses++ < 5) {poll.set(80, p);}
- else slowPoll();
+ if (!changed && !missed) {missed = true; poll.set(80, p);}
+ else {pollingFast = false; slowPoll();}
endOperation();
}
poll.set(20, p);
@@ -589,18 +605,21 @@ var CodeMirror = (function() {
function visibleLines() {
var lh = lineHeight(), top = wrapper.scrollTop - paddingTop();
- return {from: Math.max(0, Math.floor(top / lh)),
+ return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
to: Math.min(lines.length, Math.ceil((top + wrapper.clientHeight) / lh))};
}
// Uses a set of changes plus the current scroll position to
// determine which DOM updates have to be made, and makes the
// updates.
function updateDisplay(changes) {
- if (!wrapper.clientWidth) return;
+ if (!wrapper.clientWidth) {
+ showingFrom = showingTo = 0;
+ return;
+ }
// First create a range of theoretically intact lines, and punch
// holes in that using the change info.
- var intact = [{from: showingFrom, to: showingTo, domStart: 0}];
- for (var i = 0, l = changes ? changes.length : 0; i < l; ++i) {
+ var intact = changes === true ? [] : [{from: showingFrom, to: showingTo, domStart: 0}];
+ for (var i = 0, l = changes.length || 0; i < l; ++i) {
var change = changes[i], intact2 = [], diff = change.diff || 0;
for (var j = 0, l2 = intact.length; j < l2; ++j) {
var range = intact[j];
@@ -655,10 +674,11 @@ var CodeMirror = (function() {
// Position the mover div to align with the lines it's supposed
// to be showing (which will cover the visible display)
- var different = from != showingFrom || to != showingTo;
+ var different = from != showingFrom || to != showingTo || lastHeight != wrapper.clientHeight;
showingFrom = from; showingTo = to;
mover.style.top = (from * lineHeight()) + "px";
if (different) {
+ lastHeight = wrapper.clientHeight;
code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
updateGutter();
}
@@ -723,6 +743,7 @@ var CodeMirror = (function() {
}
else {
node.innerHTML = lines[j].getHTML(ch1, ch2, false);
+ node.className = lines[j].className || "";
node = node.nextSibling;
}
}
@@ -732,7 +753,8 @@ var CodeMirror = (function() {
function updateGutter() {
if (!options.gutter && !options.lineNumbers) return;
- gutter.style.height = Math.max(mover.offsetHeight, wrapper.clientHeight) + "px";
+ var hText = mover.offsetHeight, hEditor = wrapper.clientHeight;
+ gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
var html = [];
for (var i = showingFrom; i < showingTo; ++i) {
var marker = lines[i].gutterMarker;
@@ -877,9 +899,10 @@ var CodeMirror = (function() {
if (n) indentation = lines[n-1].indentation();
else indentation = 0;
}
- else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpace));
+ else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
else if (how == "add") indentation = curSpace + options.indentUnit;
- else if (how == "subtract") indentation = Math.max(0, curSpace - options.indentUnit);
+ else if (how == "subtract") indentation = curSpace - options.indentUnit;
+ indentation = Math.max(0, indentation);
var diff = indentation - curSpace;
if (!diff) {
@@ -950,6 +973,20 @@ var CodeMirror = (function() {
line.gutterMarker = null;
updateGutter();
}
+ function setLineClass(line, className) {
+ if (typeof line == "number") {
+ var no = line;
+ line = lines[clipLine(line)];
+ }
+ else {
+ var no = indexOf(lines, line);
+ if (no == -1) return null;
+ }
+ line.className = className;
+ changes.push({from: no, to: no + 1});
+ return line;
+ }
+
function lineInfo(line) {
if (typeof line == "number") {
var n = line;
@@ -1007,7 +1044,7 @@ var CodeMirror = (function() {
function lineHeight() {
var nlines = lineDiv.childNodes.length;
if (nlines) return lineDiv.offsetHeight / nlines;
- else return measure.offsetHeight || 1;
+ else return measure.firstChild.offsetHeight || 1;
}
function charWidth() {return (measure.firstChild.offsetWidth || 320) / 40;}
function paddingTop() {return lineSpace.offsetTop;}
@@ -1023,6 +1060,29 @@ var CodeMirror = (function() {
var line = showingFrom + Math.floor(y / lineHeight());
return clipPos({line: line, ch: charFromX(clipLine(line), x)});
}
+ function onContextMenu(e) {
+ var pos = posFromMouse(e);
+ if (!pos || window.opera) return; // Opera is difficult.
+ if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
+ setCursor(pos.line, pos.ch);
+
+ var oldCSS = input.style.cssText;
+ input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.pageY() - 1) +
+ "px; left: " + (e.pageX() - 1) + "px; z-index: 1000; background: white; " +
+ "border-width: 0; outline: none; overflow: hidden;";
+ var val = input.value = getSelection();
+ input.focus();
+ setSelRange(input, 0, val.length);
+ if (gecko) e.stop();
+ leaveInputAlone = true;
+ setTimeout(function() {
+ if (input.value != val) operation(replaceSelection)(input.value, "end");
+ input.style.cssText = oldCSS;
+ leaveInputAlone = false;
+ prepareInput();
+ slowPoll();
+ }, 50);
+ }
// Cursor-blinking
function restartBlink() {
@@ -1153,7 +1213,7 @@ var CodeMirror = (function() {
// updateInput can be set to a boolean value to force/prevent an
// update.
- if (updateInput === true || (updateInput !== false && selectionChanged))
+ if (!leaveInputAlone && (updateInput === true || (updateInput !== false && selectionChanged)))
prepareInput();
if (selectionChanged && options.onCursorActivity)
@@ -1332,6 +1392,18 @@ var CodeMirror = (function() {
}
return mfactory(options, config);
}
+ CodeMirror.listModes = function() {
+ var list = [];
+ for (var m in modes)
+ if (modes.propertyIsEnumerable(m)) list.push(m);
+ return list;
+ };
+ CodeMirror.listMIMEs = function() {
+ var list = [];
+ for (var m in mimeModes)
+ if (mimeModes.propertyIsEnumerable(m)) list.push(m);
+ return list;
+ };
CodeMirror.fromTextArea = function(textarea, options) {
if (!options) options = {};
@@ -1452,7 +1524,7 @@ var CodeMirror = (function() {
this.styles = styles || [text, null];
this.stateAfter = null;
this.text = text;
- this.marked = null; this.gutterMarker = null;
+ this.marked = this.gutterMarker = this.className = null;
}
Line.prototype = {
// Replace a piece of a line, keeping the styles around it intact.
@@ -1536,7 +1608,9 @@ var CodeMirror = (function() {
// Produces an HTML fragment for the line, taking selection,
// marking, and highlighting into account.
getHTML: function(sfrom, sto, includePre) {
- var html = includePre ? [""] : [];
+ var html = [];
+ if (includePre)
+ html.push(this.className ? '': "");
function span(text, style) {
if (!text) return;
if (style) html.push('', htmlEscape(text), "");
@@ -1698,6 +1772,8 @@ var CodeMirror = (function() {
pre.innerHTML = " "; return !pre.innerHTML;
})();
+ var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
+
var lineSep = "\n";
// Feature-detect whether newlines in textareas are converted to \r\n
(function () {
diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/javascript.js b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/javascript.js
index e089b0389..065216591 100644
--- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/javascript.js
+++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/CodeMirror/javascript.js
@@ -293,7 +293,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function functiondef(type, value) {
if (type == "variable") {register(value); return cont(functiondef);}
- if (type == "(") return cont(pushcontext, commasep(funarg, ")"), statement, popcontext);
+ if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
}
function funarg(type, value) {
if (type == "variable") {register(value); return cont();}
diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/CodeMirror/codemirror.css b/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/CodeMirror/codemirror.css
index cca859481..83bae5a70 100644
--- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/CodeMirror/codemirror.css
+++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/CodeMirror/codemirror.css
@@ -1,50 +1,55 @@
-.CodeMirror {
- overflow: auto;
- line-height: 1em;
- font-family: Consolas, Monospace;
- font-size:10pt;
- _position: relative; /* IE6 hack */
-}
-
-.CodeMirror-gutter {
- position: absolute; left: 0; top: 0;
- background-color: #f7f7f7;
- border-right: 1px solid #eee;
- min-width: 2em;
- height: 100%;
-}
-.CodeMirror-gutter-text {
- color: #aaa;
- text-align: right;
- padding: .4em .2em .4em .4em;
-}
-.CodeMirror-lines {
- padding: .4em;
-}
-
-.CodeMirror pre {
- border-width: 0; margin: 0; padding: 0; background: transparent;
- font-family: inherit;
-}
-
-.CodeMirror-cursor {
- z-index: 10;
- position: absolute;
- visibility: hidden;
- border-left: 1px solid black !important;
-}
-.CodeMirror-focused .CodeMirror-cursor {
- visibility: visible;
-}
-
-span.CodeMirror-selected {
- background: #ccc !important;
- color: HighlightText !important;
-}
-.CodeMirror-focused span.CodeMirror-selected {
- background: Highlight !important;
-}
-
-
-.CodeMirror-matchingbracket {color: #0f0 !important;}
-.CodeMirror-nonmatchingbracket {color: #f22 !important;}
+.CodeMirror {
+ overflow: auto;
+ line-height: 1em;
+ font-family: Consolas, Monospace;
+ font-size:10pt;
+ _position: relative; /* IE6 hack */
+}
+
+.CodeMirror-gutter {
+ position: absolute; left: 0; top: 0;
+ background-color: #f7f7f7;
+ border-right: 1px solid #eee;
+ min-width: 2em;
+ height: 100%;
+}
+.CodeMirror-gutter-text {
+ color: #aaa;
+ text-align: right;
+ padding: .4em .2em .4em .4em;
+}
+.CodeMirror-lines {
+ padding: .4em;
+}
+
+.CodeMirror pre
+{
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ -o-border-radius: 0;
+ border-radius: 0;
+ border-width: 0; margin: 0; padding: 0; background: transparent;
+ font-family: inherit;
+}
+
+.CodeMirror-cursor {
+ z-index: 10;
+ position: absolute;
+ visibility: hidden;
+ border-left: 1px solid black !important;
+}
+.CodeMirror-focused .CodeMirror-cursor {
+ visibility: visible;
+}
+
+span.CodeMirror-selected {
+ background: #ccc !important;
+ color: HighlightText !important;
+}
+.CodeMirror-focused span.CodeMirror-selected {
+ background: Highlight !important;
+}
+
+
+.CodeMirror-matchingbracket {color: #0f0 !important;}
+.CodeMirror-nonmatchingbracket {color: #f22 !important;}