# HG changeset patch # User Marcin Kuzminski # Date 2013-06-26 12:29:49 # Node ID af3c746b3f1f38cac486037d4af44a3745c2f80c # Parent 15f4825890fe526fc7659b24ba8c325c2fa452b0 update codemirror to latest version diff --git a/rhodecode/public/css/codemirror.css b/rhodecode/public/css/codemirror.css --- a/rhodecode/public/css/codemirror.css +++ b/rhodecode/public/css/codemirror.css @@ -19,7 +19,7 @@ padding: 0 4px; /* Horizontal padding of content */ } -.CodeMirror-scrollbar-filler { +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; /* The little square between H and V scrollbars */ } @@ -28,6 +28,7 @@ .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: #f7f7f7; + white-space: nowrap; } .CodeMirror-linenumbers {} .CodeMirror-linenumber { @@ -124,7 +125,7 @@ div.CodeMirror span.CodeMirror-nonmatchi /* The fake, visible scrollbars. Used to force redraw during scrolling before actuall scrolling happens, thus preventing shaking and flickering artifacts. */ -.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler { +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; z-index: 6; display: none; @@ -141,16 +142,18 @@ div.CodeMirror span.CodeMirror-nonmatchi } .CodeMirror-scrollbar-filler { right: 0; bottom: 0; - z-index: 6; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; } .CodeMirror-gutters { position: absolute; left: 0; top: 0; - height: 100%; padding-bottom: 30px; z-index: 3; } .CodeMirror-gutter { + white-space: normal; height: 100%; padding-bottom: 30px; margin-bottom: -32px; @@ -202,7 +205,6 @@ div.CodeMirror span.CodeMirror-nonmatchi } .CodeMirror-widget { - display: inline-block; } .CodeMirror-wrap .CodeMirror-scroll { diff --git a/rhodecode/public/js/codemirror.js b/rhodecode/public/js/codemirror.js --- a/rhodecode/public/js/codemirror.js +++ b/rhodecode/public/js/codemirror.js @@ -1,3 +1,5 @@ +// CodeMirror version 3.14 +// // CodeMirror is the only global var we claim window.CodeMirror = (function() { "use strict"; @@ -97,7 +99,7 @@ window.CodeMirror = (function() { else input.setAttribute("wrap", "off"); // if border: 0; -- iOS fails to open keyboard (issue #1287) if (ios) input.style.border = "1px solid black"; - input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); + input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false"); // Wraps and hides input textarea d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); @@ -105,8 +107,9 @@ window.CodeMirror = (function() { d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar"); d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); // DIVs containing the selection and the actual code - d.lineDiv = elt("div"); + d.lineDiv = elt("div", null, "CodeMirror-code"); d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); // Blinky cursor, and element used to ensure cursor fits at the end of a line d.cursor = elt("div", "\u00a0", "CodeMirror-cursor"); @@ -126,14 +129,12 @@ window.CodeMirror = (function() { // Will contain the gutters, if any d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; - // Helper element to properly size the gutter backgrounds - var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%"); // Provides scrolling - d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll"); + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV, - d.scrollbarFiller, d.scroller], "CodeMirror"); + d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper); @@ -212,7 +213,7 @@ window.CodeMirror = (function() { estimateLineHeights(cm); regChange(cm); clearCaches(cm); - setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100); + setTimeout(function(){updateScrollbars(cm);}, 100); } function estimateHeight(cm) { @@ -237,9 +238,10 @@ window.CodeMirror = (function() { } function keyMapChanged(cm) { - var style = keyMap[cm.options.keyMap].style; + var map = keyMap[cm.options.keyMap], style = map.style; cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + (style ? " cm-keymap-" + style : ""); + cm.state.disableInput = map.disableInput; } function themeChanged(cm) { @@ -251,6 +253,7 @@ window.CodeMirror = (function() { function guttersChanged(cm) { updateGutters(cm); regChange(cm); + setTimeout(function(){alignHorizontally(cm);}, 20); } function updateGutters(cm) { @@ -317,12 +320,14 @@ window.CodeMirror = (function() { // Re-synchronize the fake scrollbars with the actual size of the // content. Optionally force a scrollTop. - function updateScrollbars(d /* display */, docHeight) { + function updateScrollbars(cm) { + var d = cm.display, docHeight = cm.doc.height; var totalHeight = docHeight + paddingVert(d); d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px"; + d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px"; var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight); - var needsH = d.scroller.scrollWidth > d.scroller.clientWidth; - var needsV = scrollHeight > d.scroller.clientHeight; + var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1); + var needsV = scrollHeight > (d.scroller.clientHeight + 1); if (needsV) { d.scrollbarV.style.display = "block"; d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; @@ -339,6 +344,11 @@ window.CodeMirror = (function() { d.scrollbarFiller.style.display = "block"; d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px"; } else d.scrollbarFiller.style.display = ""; + if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px"; + d.gutterFiller.style.width = d.gutters.offsetWidth + "px"; + } else d.gutterFiller.style.display = ""; if (mac_geLion && scrollbarWidth(d.measure) === 0) d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px"; @@ -397,14 +407,10 @@ window.CodeMirror = (function() { var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; var visible = visibleLines(cm.display, cm.doc, viewPort); for (;;) { - if (updateDisplayInner(cm, changes, visible)) { - updated = true; - signalLater(cm, "update", cm); - if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) - signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); - } else break; + if (!updateDisplayInner(cm, changes, visible)) break; + updated = true; updateSelection(cm); - updateScrollbars(cm.display, cm.doc.height); + updateScrollbars(cm); // Clip forced viewport to actual scrollable area if (viewPort) @@ -416,6 +422,11 @@ window.CodeMirror = (function() { changes = []; } + if (updated) { + signalLater(cm, "update", cm); + if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) + signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); + } return updated; } @@ -503,9 +514,11 @@ window.CodeMirror = (function() { display.lastSizeC != display.wrapper.clientHeight; // This is just a bogus formula that detects when the editor is // resized or the font size changes. - if (different) display.lastSizeC = display.wrapper.clientHeight; + if (different) { + display.lastSizeC = display.wrapper.clientHeight; + startWorker(cm, 400); + } display.showingFrom = from; display.showingTo = to; - startWorker(cm, 100); var prevBottom = display.lineDiv.offsetTop; for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) { @@ -594,8 +607,9 @@ window.CodeMirror = (function() { if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift(); if (lineIsHidden(cm.doc, line)) { if (line.height != 0) updateLineHeight(line, 0); - if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) - if (line.widgets[i].showIfHidden) { + if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i]; + if (w.showIfHidden) { var prev = cur.previousSibling; if (/pre/i.test(prev.nodeName)) { var wrap = elt("div", null, null, "position: relative"); @@ -603,9 +617,11 @@ window.CodeMirror = (function() { wrap.appendChild(prev); prev = wrap; } - var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget")); - positionLineWidget(line.widgets[i], wnode, prev, dims); + var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget")); + if (!w.handleMouseEvents) wnode.ignoreEvents = true; + positionLineWidget(w, wnode, prev, dims); } + } } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) { // This line is intact. Skip to the actual node. Update its // line number if needed. @@ -648,25 +664,25 @@ window.CodeMirror = (function() { if (reuse) { reuse.alignable = null; - var isOk = true, widgetsSeen = 0; + var isOk = true, widgetsSeen = 0, insertBefore = null; for (var n = reuse.firstChild, next; n; n = next) { next = n.nextSibling; if (!/\bCodeMirror-linewidget\b/.test(n.className)) { reuse.removeChild(n); } else { for (var i = 0, first = true; i < line.widgets.length; ++i) { - var widget = line.widgets[i], isFirst = false; - if (!widget.above) { isFirst = first; first = false; } + var widget = line.widgets[i]; + if (!widget.above) { insertBefore = n; first = false; } if (widget.node == n.firstChild) { positionLineWidget(widget, n, reuse, dims); ++widgetsSeen; - if (isFirst) reuse.insertBefore(lineElement, n); break; } } if (i == line.widgets.length) { isOk = false; break; } } } + reuse.insertBefore(lineElement, insertBefore); if (isOk && widgetsSeen == line.widgets.length) { wrap = reuse; reuse.className = line.wrapClass || ""; @@ -701,6 +717,7 @@ window.CodeMirror = (function() { if (ie_lt8) wrap.style.zIndex = 2; if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + if (!widget.handleMouseEvents) node.ignoreEvents = true; positionLineWidget(widget, node, wrap, dims); if (widget.above) wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement); @@ -783,74 +800,59 @@ window.CodeMirror = (function() { "px; height: " + (bottom - top) + "px")); } - function drawForLine(line, fromArg, toArg, retTop) { + function drawForLine(line, fromArg, toArg) { var lineObj = getLine(doc, line); - var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity; - function coords(ch) { - return charCoords(cm, Pos(line, ch), "div", lineObj); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias); } iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { - var leftPos = coords(from), rightPos, left, right; + var leftPos = coords(from, "left"), rightPos, left, right; if (from == to) { rightPos = leftPos; left = right = leftPos.left; } else { - rightPos = coords(to - 1); + rightPos = coords(to - 1, "right"); if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } left = leftPos.left; right = rightPos.right; } + if (fromArg == null && from == 0) left = pl; if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part add(left, leftPos.top, null, leftPos.bottom); left = pl; if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); } if (toArg == null && to == lineLen) right = clientWidth; - if (fromArg == null && from == 0) left = pl; - rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal); + if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) + start = leftPos; + if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) + end = rightPos; if (left < pl + 1) left = pl; add(left, rightPos.top, right - left, rightPos.bottom); }); - return rVal; + return {start: start, end: end}; } if (sel.from.line == sel.to.line) { drawForLine(sel.from.line, sel.from.ch, sel.to.ch); } else { - var fromObj = getLine(doc, sel.from.line); - var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine; - while (merged = collapsedSpanAtEnd(cur)) { - var found = merged.find(); - path.push(found.from.ch, found.to.line, found.to.ch); - if (found.to.line == sel.to.line) { - path.push(sel.to.ch); - singleLine = true; - break; + var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line); + var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine); + var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end; + var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(pl, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); } - cur = getLine(doc, found.to.line); } - - // This is a single, merged line - if (singleLine) { - for (var i = 0; i < path.length; i += 3) - drawForLine(path[i], path[i+1], path[i+2]); - } else { - var middleTop, middleBot, toObj = getLine(doc, sel.to.line); - if (sel.from.ch) - // Draw the first line of selection. - middleTop = drawForLine(sel.from.line, sel.from.ch, null, false); - else - // Simply include it in the middle block. - middleTop = heightAtLine(cm, fromObj) - display.viewOffset; - - if (!sel.to.ch) - middleBot = heightAtLine(cm, toObj) - display.viewOffset; - else - middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true); - - if (middleTop < middleBot) add(pl, middleTop, null, middleBot); - } + if (leftEnd.bottom < rightStart.top) + add(pl, leftEnd.bottom, null, rightStart.top); } removeChildrenAndAdd(display.selectionDiv, fragment); @@ -916,12 +918,12 @@ window.CodeMirror = (function() { // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. - function findStartLine(cm, n) { + function findStartLine(cm, n, precise) { var minindent, minline, doc = cm.doc; for (var search = n, lim = n - 100; search > lim; --search) { if (search <= doc.first) return doc.first; var line = getLine(doc, search - 1); - if (line.stateAfter) return search; + if (line.stateAfter && (!precise || search <= doc.frontier)) return search; var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; @@ -931,10 +933,10 @@ window.CodeMirror = (function() { return minline; } - function getStateBefore(cm, n) { + function getStateBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; if (!doc.mode.startState) return true; - var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter; + var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter; if (!state) state = startState(doc.mode); else state = copyState(doc.mode, state); doc.iter(pos, n, function(line) { @@ -955,7 +957,7 @@ window.CodeMirror = (function() { return e.offsetLeft; } - function measureChar(cm, line, ch, data) { + function measureChar(cm, line, ch, data, bias) { var dir = -1; data = data || measureLine(cm, line); @@ -964,9 +966,11 @@ window.CodeMirror = (function() { if (r) break; if (dir < 0 && pos == 0) dir = 1; } + var rightV = (pos < ch || bias == "right") && r.topRight != null; return {left: pos < ch ? r.right : r.left, right: pos > ch ? r.left : r.right, - top: r.top, bottom: r.bottom}; + top: rightV ? r.topRight : r.top, + bottom: rightV ? r.bottomRight : r.bottom}; } function findCachedMeasurement(cm, line) { @@ -976,23 +980,28 @@ window.CodeMirror = (function() { if (memo.text == line.text && memo.markedSpans == line.markedSpans && cm.display.scroller.clientWidth == memo.width && memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass) - return memo.measure; - } + return memo; + } + } + + function clearCachedMeasurement(cm, line) { + var exists = findCachedMeasurement(cm, line); + if (exists) exists.text = exists.measure = exists.markedSpans = null; } function measureLine(cm, line) { // First look in the cache - var measure = findCachedMeasurement(cm, line); - if (!measure) { - // Failing that, recompute and store result in cache - measure = measureLineInner(cm, line); - var cache = cm.display.measureLineCache; - var memo = {text: line.text, width: cm.display.scroller.clientWidth, - markedSpans: line.markedSpans, measure: measure, - classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass}; - if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo; - else cache.push(memo); - } + var cached = findCachedMeasurement(cm, line); + if (cached) return cached.measure; + + // Failing that, recompute and store result in cache + var measure = measureLineInner(cm, line); + var cache = cm.display.measureLineCache; + var memo = {text: line.text, width: cm.display.scroller.clientWidth, + markedSpans: line.markedSpans, measure: measure, + classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass}; + if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo; + else cache.push(memo); return measure; } @@ -1034,9 +1043,9 @@ window.CodeMirror = (function() { if (ie_lt9 && display.measure.first != pre) removeChildrenAndAdd(display.measure, pre); - for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) { - var size = getRect(cur); - var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot); + function categorizeVSpan(top, bot) { + if (bot > maxBot) bot = maxBot; + if (top < 0) top = 0; for (var j = 0; j < vranges.length; j += 2) { var rtop = vranges[j], rbot = vranges[j+1]; if (rtop > bot || rbot < top) continue; @@ -1045,19 +1054,38 @@ window.CodeMirror = (function() { Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) { vranges[j] = Math.min(top, rtop); vranges[j+1] = Math.max(bot, rbot); - break; + return j; } } - if (j == vranges.length) vranges.push(top, bot); + vranges.push(top, bot); + return j; + } + + for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) { + var size, node = cur; + // A widget might wrap, needs special care + if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) { + if (cur.firstChild.nodeType == 1) node = cur.firstChild; + var rects = node.getClientRects(), rLeft = rects[0], rRight = rects[rects.length - 1]; + if (rects.length > 1) { + var vCatLeft = categorizeVSpan(rLeft.top - outer.top, rLeft.bottom - outer.top); + var vCatRight = categorizeVSpan(rRight.top - outer.top, rRight.bottom - outer.top); + data[i] = {left: rLeft.left - outer.left, right: rRight.right - outer.left, + top: vCatLeft, topRight: vCatRight}; + continue; + } + } + size = getRect(node); + var vCat = categorizeVSpan(size.top - outer.top, size.bottom - outer.top); var right = size.right; if (cur.measureRight) right = getRect(cur.measureRight).left; - data[i] = {left: size.left - outer.left, right: right - outer.left, top: j}; + data[i] = {left: size.left - outer.left, right: right - outer.left, top: vCat}; } for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) { - var vr = cur.top; + var vr = cur.top, vrRight = cur.topRight; cur.top = vranges[vr]; cur.bottom = vranges[vr+1]; - } - + if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight+1]; } + } return data; } @@ -1068,7 +1096,7 @@ window.CodeMirror = (function() { if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true; } var cached = !hasBadSpan && findCachedMeasurement(cm, line); - if (cached) return measureChar(cm, line, line.text.length, cached).right; + if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right; var pre = lineContent(cm, line); var end = pre.appendChild(zeroWidthElement(cm.display.measure)); @@ -1083,6 +1111,9 @@ window.CodeMirror = (function() { cm.display.lineNumChars = null; } + function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; } + function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; } + // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" function intoCoordSystem(cm, lineObj, rect, context) { if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { @@ -1092,11 +1123,12 @@ window.CodeMirror = (function() { if (context == "line") return rect; if (!context) context = "local"; var yOff = heightAtLine(cm, lineObj); - if (context != "local") yOff -= cm.display.viewOffset; - if (context == "page") { + if (context == "local") yOff += paddingTop(cm.display); + else yOff -= cm.display.viewOffset; + if (context == "page" || context == "window") { var lOff = getRect(cm.display.lineSpace); - yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop); - var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; @@ -1108,64 +1140,58 @@ window.CodeMirror = (function() { function fromCoordSystem(cm, coords, context) { if (context == "div") return coords; var left = coords.left, top = coords.top; + // First move into "page" coordinate system if (context == "page") { - left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft; - top -= window.pageYOffset || (document.documentElement || document.body).scrollTop; - } + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = getRect(cm.display.sizer); + left += localBox.left; + top += localBox.top; + } + var lineSpaceBox = getRect(cm.display.lineSpace); - left -= lineSpaceBox.left; - top -= lineSpaceBox.top; - if (context == "local" || !context) { - var editorBox = getRect(cm.display.wrapper); - left += editorBox.left; - top += editorBox.top; - } - return {left: left, top: top}; - } - - function charCoords(cm, pos, context, lineObj) { + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; + } + + function charCoords(cm, pos, context, lineObj, bias) { if (!lineObj) lineObj = getLine(cm.doc, pos.line); - return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context); + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context); } function cursorCoords(cm, pos, context, lineObj, measurement) { lineObj = lineObj || getLine(cm.doc, pos.line); if (!measurement) measurement = measureLine(cm, lineObj); function get(ch, right) { - var m = measureChar(cm, lineObj, ch, measurement); + var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left"); if (right) m.left = m.right; else m.right = m.left; return intoCoordSystem(cm, lineObj, m, context); } + function getBidi(ch, partPos) { + var part = order[partPos], right = part.level % 2; + if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { + part = order[--partPos]; + ch = bidiRight(part) - (part.level % 2 ? 0 : 1); + right = true; + } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) { + part = order[++partPos]; + ch = bidiLeft(part) - part.level % 2; + right = false; + } + if (right && ch == part.to && ch > part.from) return get(ch - 1); + return get(ch, right); + } var order = getOrder(lineObj), ch = pos.ch; if (!order) return get(ch); - var main, other, linedir = order[0].level; - for (var i = 0; i < order.length; ++i) { - var part = order[i], rtl = part.level % 2, nb, here; - if (part.from < ch && part.to > ch) return get(ch, rtl); - var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; - if (left == ch) { - // IE returns bogus offsets and widths for edges where the - // direction flips, but only for the side with the lower - // level. So we try to use the side with the higher level. - if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); - else here = get(rtl && part.from != part.to ? ch - 1 : ch); - if (rtl == linedir) main = here; else other = here; - } else if (right == ch) { - var nb = i < order.length - 1 && order[i+1]; - if (!rtl && nb && nb.from == nb.to) continue; - if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from); - else here = get(rtl ? ch : ch - 1, true); - if (rtl == linedir) main = here; else other = here; - } - } - if (linedir && !ch) other = get(order[0].to - 1); - if (!main) return other; - if (other) main.other = other; - return main; - } - - function PosMaybeOutside(line, ch, outside) { + var partPos = getBidiPartAt(order, ch); + var val = getBidi(ch, partPos); + if (bidiOther != null) val.other = getBidi(ch, bidiOther); + return val; + } + + function PosWithInfo(line, ch, outside, xRel) { var pos = new Pos(line, ch); + pos.xRel = xRel; if (outside) pos.outside = true; return pos; } @@ -1174,10 +1200,10 @@ window.CodeMirror = (function() { function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; - if (y < 0) return PosMaybeOutside(doc.first, 0, true); + if (y < 0) return PosWithInfo(doc.first, 0, true, -1); var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineNo > last) - return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true); + return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1); if (x < 0) x = 0; for (;;) { @@ -1185,7 +1211,7 @@ window.CodeMirror = (function() { var found = coordsCharInner(cm, lineObj, lineNo, x, y); var merged = collapsedSpanAtEnd(lineObj); var mergedPos = merged && merged.find(); - if (merged && found.ch >= mergedPos.from.ch) + if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) lineNo = mergedPos.to.line; else return found; @@ -1211,14 +1237,15 @@ window.CodeMirror = (function() { var from = lineLeft(lineObj), to = lineRight(lineObj); var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine; - if (x > toX) return PosMaybeOutside(lineNo, to, toOutside); + if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1); // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { - var after = x - fromX < toX - x, ch = after ? from : to; + var ch = x < fromX || x - fromX <= toX - x ? from : to; + var xDiff = x - (ch == from ? fromX : toX); while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; - var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside); - pos.after = after; + var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside, + xDiff < 0 ? -1 : xDiff ? 1 : 0); return pos; } var step = Math.ceil(dist / 2), middle = from + step; @@ -1404,7 +1431,7 @@ window.CodeMirror = (function() { // supported or compatible enough yet to rely on.) function readInput(cm) { var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel; - if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false; + if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false; var text = input.value; if (text == prevInput && posEq(sel.from, sel.to)) return false; if (ie && !ie_lt9 && cm.display.inputHasSelection === text) { @@ -1422,11 +1449,14 @@ window.CodeMirror = (function() { from = Pos(from.line, from.ch - (prevInput.length - same)); else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming) to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same))); + var updateInput = cm.curOp.updateInput; - makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)), - origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end"); - + var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)), + origin: cm.state.pasteIncoming ? "paste" : "+input"}; + makeChange(cm.doc, changeEvent, "end"); cm.curOp.updateInput = updateInput; + signalLater(cm, "inputRead", cm, changeEvent); + if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = ""; else cm.display.prevInput = text; if (withOp) endOperation(cm); @@ -1467,6 +1497,7 @@ window.CodeMirror = (function() { on(d.scroller, "mousedown", operation(cm, onMouseDown)); if (ie) on(d.scroller, "dblclick", operation(cm, function(e) { + if (signalDOMEvent(cm, e)) return; var pos = posFromMouse(cm, e); if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; e_preventDefault(e); @@ -1474,7 +1505,7 @@ window.CodeMirror = (function() { extendSelection(cm.doc, word.from, word.to); })); else - on(d.scroller, "dblclick", e_preventDefault); + on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); }); on(d.lineSpace, "selectstart", function(e) { if (!eventInWidget(d, e)) e_preventDefault(e); }); @@ -1506,11 +1537,15 @@ window.CodeMirror = (function() { // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + var resizeTimer; function onResize() { - // Might be a text scaling operation, clear size caches. - d.cachedCharWidth = d.cachedTextHeight = null; - clearCaches(cm); - runInOp(cm, bind(regChange, cm)); + if (resizeTimer == null) resizeTimer = setTimeout(function() { + resizeTimer = null; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null; + clearCaches(cm); + runInOp(cm, bind(regChange, cm)); + }, 100); } on(window, "resize", onResize); // Above handler holds on to the editor and its data structures. @@ -1524,7 +1559,7 @@ window.CodeMirror = (function() { setTimeout(unregister, 5000); on(d.input, "keyup", operation(cm, function(e) { - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; if (e.keyCode == 16) cm.doc.sel.shift = false; })); on(d.input, "input", bind(fastPoll, cm)); @@ -1534,7 +1569,7 @@ window.CodeMirror = (function() { on(d.input, "blur", bind(onBlur, cm)); function drag_(e) { - if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return; e_stop(e); } if (cm.options.dragDrop) { @@ -1573,9 +1608,7 @@ window.CodeMirror = (function() { function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { - if (!n) return true; - if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) || - n.parentNode == display.sizer && n != display.mover) return true; + if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true; } } @@ -1585,7 +1618,7 @@ window.CodeMirror = (function() { var target = e_target(e); if (target == display.scrollbarH || target == display.scrollbarH.firstChild || target == display.scrollbarV || target == display.scrollbarV.firstChild || - target == display.scrollbarFiller) return null; + target == display.scrollbarFiller || target == display.gutterFiller) return null; } var x, y, space = getRect(display.lineSpace); // Fails unpredictably on IE[67] when mouse is dragged around quickly. @@ -1595,6 +1628,7 @@ window.CodeMirror = (function() { var lastClick, lastDoubleClick; function onMouseDown(e) { + if (signalDOMEvent(this, e)) return; var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel; sel.shift = e.shiftKey; @@ -1718,8 +1752,6 @@ window.CodeMirror = (function() { function done(e) { counter = Infinity; - var cur = posFromMouse(cm, e); - if (cur) doSelect(cur); e_preventDefault(e); focusInput(cm); off(document, "mousemove", move); @@ -1735,11 +1767,41 @@ window.CodeMirror = (function() { on(document, "mouseup", up); } + function clickInGutter(cm, e) { + var display = cm.display; + try { var mX = e.clientX, mY = e.clientY; } + catch(e) { return false; } + + if (mX >= Math.floor(getRect(display.gutters).right)) return false; + e_preventDefault(e); + if (!hasHandler(cm, "gutterClick")) return true; + + var lineBox = getRect(display.lineDiv); + if (mY > lineBox.bottom) return true; + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && getRect(g).right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.options.gutters[i]; + signalLater(cm, "gutterClick", cm, line, gutter, e); + break; + } + } + return true; + } + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + function onDrop(e) { var cm = this; - if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e)))) + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e)))) return; e_preventDefault(e); + if (ie) lastDrop = +new Date; var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; if (!pos || isReadOnly(cm)) return; if (files && files.length && window.FileReader && window.File) { @@ -1779,41 +1841,16 @@ window.CodeMirror = (function() { } } - function clickInGutter(cm, e) { - var display = cm.display; - try { var mX = e.clientX, mY = e.clientY; } - catch(e) { return false; } - - if (mX >= Math.floor(getRect(display.gutters).right)) return false; - e_preventDefault(e); - if (!hasHandler(cm, "gutterClick")) return true; - - var lineBox = getRect(display.lineDiv); - if (mY > lineBox.bottom) return true; - mY -= lineBox.top - display.viewOffset; - - for (var i = 0; i < cm.options.gutters.length; ++i) { - var g = display.gutters.childNodes[i]; - if (g && getRect(g).right >= mX) { - var line = lineAtHeight(cm.doc, mY); - var gutter = cm.options.gutters[i]; - signalLater(cm, "gutterClick", cm, line, gutter, e); - break; - } - } - return true; - } - function onDragStart(cm, e) { - if (ie && !cm.state.draggingText) { e_stop(e); return; } - if (eventInWidget(cm.display, e)) return; + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; var txt = cm.getSelection(); e.dataTransfer.setData("Text", txt); // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. - if (e.dataTransfer.setDragImage) { + if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); if (opera) { img.width = img.height = 1; @@ -1821,15 +1858,6 @@ window.CodeMirror = (function() { // Force a relayout, or Opera won't use our image for some obscure reason img._top = img.offsetTop; } - if (safari) { - if (cm.display.dragImg) { - img = cm.display.dragImg; - } else { - cm.display.dragImg = img; - img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; - cm.display.wrapper.appendChild(img); - } - } e.dataTransfer.setDragImage(img, 0, 0); if (opera) img.parentNode.removeChild(img); } @@ -1842,6 +1870,7 @@ window.CodeMirror = (function() { if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val; if (gecko) updateDisplay(cm, []); + startWorker(cm, 100); } function setScrollLeft(cm, val, isScroller) { if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return; @@ -1974,8 +2003,10 @@ window.CodeMirror = (function() { var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto; clearTimeout(maybeTransition); if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { - if (getKeyMap(cm.options.keyMap) == startMap) + if (getKeyMap(cm.options.keyMap) == startMap) { cm.options.keyMap = (next.call ? next.call(null, cm) : next); + keyMapChanged(cm); + } }, 50); var name = keyName(e, true), handled = false; @@ -1988,17 +2019,18 @@ window.CodeMirror = (function() { // 'go') bound to the keyname without 'Shift-'. handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);}) || lookupKey(name, keymaps, function(b) { - if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b); + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + return doHandleBinding(cm, b); }); } else { handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); }); } - if (handled == "stop") handled = false; if (handled) { e_preventDefault(e); restartBlink(cm); if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } + signalLater(cm, "keyHandled", cm, name, e); } return handled; } @@ -2009,6 +2041,7 @@ window.CodeMirror = (function() { if (handled) { e_preventDefault(e); restartBlink(cm); + signalLater(cm, "keyHandled", cm, "'" + ch + "'", e); } return handled; } @@ -2018,7 +2051,7 @@ window.CodeMirror = (function() { var cm = this; if (!cm.state.focused) onFocus(cm); if (ie && e.keyCode == 27) { e.returnValue = false; } - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; var code = e.keyCode; // IE does strange things with escape. cm.doc.sel.shift = code == 16 || e.shiftKey; @@ -2034,7 +2067,7 @@ window.CodeMirror = (function() { function onKeyPress(e) { var cm = this; - if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; var keyCode = e.keyCode, charCode = e.charCode; if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; @@ -2090,6 +2123,13 @@ window.CodeMirror = (function() { // Adds "Select all" to context menu in FF if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " "; + function prepareSelectAllHack() { + if (display.input.selectionStart != null) { + var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value); + display.prevInput = " "; + display.input.selectionStart = 1; display.input.selectionEnd = extval.length; + } + } function rehide() { display.inputDiv.style.position = "relative"; display.input.style.cssText = oldCSS; @@ -2097,12 +2137,10 @@ window.CodeMirror = (function() { slowPoll(cm); // Try to detect the user choosing select-all - if (display.input.selectionStart != null && (!ie || ie_lt9)) { + if (display.input.selectionStart != null) { + if (!ie || ie_lt9) prepareSelectAllHack(); clearTimeout(detectingSelectAll); - var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0; - display.prevInput = " "; - display.input.selectionStart = 1; display.input.selectionEnd = extval.length; - var poll = function(){ + var i = 0, poll = function(){ if (display.prevInput == " " && display.input.selectionStart == 0) operation(cm, commands.selectAll)(cm); else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); @@ -2112,6 +2150,7 @@ window.CodeMirror = (function() { } } + if (ie && !ie_lt9) prepareSelectAllHack(); if (captureMiddleClick) { e_stop(e); var mouseup = function() { @@ -2126,11 +2165,11 @@ window.CodeMirror = (function() { // UPDATING - function changeEnd(change) { + var changeEnd = CodeMirror.changeEnd = function(change) { if (!change.text) return change.to; return Pos(change.from.line + change.text.length - 1, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); - } + }; // Make sure a position will be valid after the given change. function clipPostChange(doc, change, pos) { @@ -2172,21 +2211,21 @@ window.CodeMirror = (function() { return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)}; } - function filterChange(doc, change) { + function filterChange(doc, change, update) { var obj = { canceled: false, from: change.from, to: change.to, text: change.text, origin: change.origin, - update: function(from, to, text, origin) { - if (from) this.from = clipPos(doc, from); - if (to) this.to = clipPos(doc, to); - if (text) this.text = text; - if (origin !== undefined) this.origin = origin; - }, cancel: function() { this.canceled = true; } }; + if (update) obj.update = function(from, to, text, origin) { + if (from) this.from = clipPos(doc, from); + if (to) this.to = clipPos(doc, to); + if (text) this.text = text; + if (origin !== undefined) this.origin = origin; + }; signal(doc, "beforeChange", doc, obj); if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); @@ -2203,7 +2242,7 @@ window.CodeMirror = (function() { } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { - change = filterChange(doc, change); + change = filterChange(doc, change, true); if (!change) return; } @@ -2242,15 +2281,23 @@ window.CodeMirror = (function() { var hist = doc.history; var event = (type == "undo" ? hist.done : hist.undone).pop(); if (!event) return; - hist.dirtyCounter += type == "undo" ? -1 : 1; var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter, - anchorAfter: event.anchorBefore, headAfter: event.headBefore}; + anchorAfter: event.anchorBefore, headAfter: event.headBefore, + generation: hist.generation}; (type == "undo" ? hist.undone : hist.done).push(anti); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); for (var i = event.changes.length - 1; i >= 0; --i) { var change = event.changes[i]; change.origin = type; + if (filter && !filterChange(doc, change, false)) { + (type == "undo" ? hist.done : hist.undone).length = 0; + return; + } + anti.changes.push(historyChangeFromChange(doc, change)); var after = i ? computeSelAfterChange(doc, change, null) @@ -2512,11 +2559,11 @@ window.CodeMirror = (function() { // SCROLLING function scrollCursorIntoView(cm) { - var coords = scrollPosIntoView(cm, cm.doc.sel.head); + var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin); if (!cm.state.focused) return; - var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display); - if (coords.top + pTop + box.top < 0) doScroll = true; - else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + var display = cm.display, box = getRect(display.sizer), doScroll = null; + if (coords.top + box.top < 0) doScroll = true; + else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; if (doScroll != null && !phantom) { var hidden = display.cursor.style.display == "none"; if (hidden) { @@ -2554,12 +2601,11 @@ window.CodeMirror = (function() { } function calculateScrollPos(cm, x1, y1, x2, y2) { - var display = cm.display, pt = paddingTop(display); - y1 += pt; y2 += pt; + var display = cm.display, snapMargin = textHeight(cm.display); if (y1 < 0) y1 = 0; var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {}; var docBottom = cm.doc.height + paddingVert(display); - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; + var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; if (y1 < screentop) { result.scrollTop = atTop ? 0 : y1; } else if (y2 > screentop + screen) { @@ -2596,7 +2642,7 @@ window.CodeMirror = (function() { function indentLine(cm, n, how, aggressive) { var doc = cm.doc; - if (!how) how = "add"; + if (how == null) how = "add"; if (how == "smart") { if (!cm.doc.mode.indent) how = "prev"; else var state = getStateBefore(cm, n); @@ -2619,6 +2665,8 @@ window.CodeMirror = (function() { indentation = curSpace + cm.options.indentUnit; } else if (how == "subtract") { indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; } indentation = Math.max(0, indentation); @@ -2643,7 +2691,7 @@ window.CodeMirror = (function() { } function findPosH(doc, pos, dir, unit, visually) { - var line = pos.line, ch = pos.ch; + var line = pos.line, ch = pos.ch, origDir = dir; var lineObj = getLine(doc, line); var possible = true; function findNextLine() { @@ -2682,7 +2730,7 @@ window.CodeMirror = (function() { if (dir > 0 && !moveOnce(!first)) break; } } - var result = skipAtomic(doc, Pos(line, ch), dir, true); + var result = skipAtomic(doc, Pos(line, ch), origDir, true); if (!possible) result.hitSide = true; return result; } @@ -2707,7 +2755,7 @@ window.CodeMirror = (function() { function findWordAt(line, pos) { var start = pos.ch, end = pos.ch; if (line) { - if (pos.after === false || end == line.length) --start; else ++end; + if (pos.xRel < 0 || end == line.length) --start; else ++end; var startChar = line.charAt(start); var check = isWordChar(startChar) ? isWordChar : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} @@ -2728,6 +2776,7 @@ window.CodeMirror = (function() { // 'wrap f in an operation, performed on its `this` parameter' CodeMirror.prototype = { + constructor: CodeMirror, focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);}, setOption: function(option, value) { @@ -2763,7 +2812,8 @@ window.CodeMirror = (function() { removeOverlay: operation(null, function(spec) { var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { - if (overlays[i].modeSpec == spec) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); this.state.modeGen++; regChange(this); @@ -2773,7 +2823,7 @@ window.CodeMirror = (function() { }), indentLine: operation(null, function(n, dir, aggressive) { - if (typeof dir != "string") { + if (typeof dir != "string" && typeof dir != "number") { if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; else dir = dir ? "add" : "subtract"; } @@ -2788,10 +2838,10 @@ window.CodeMirror = (function() { // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). - getTokenAt: function(pos) { + getTokenAt: function(pos, precise) { var doc = this.doc; pos = clipPos(doc, pos); - var state = getStateBefore(this, pos.line), mode = this.doc.mode; + var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode; var line = getLine(doc, pos.line); var stream = new StringStream(line.text, this.options.tabSize); while (stream.pos < pos.ch && !stream.eol()) { @@ -2806,10 +2856,22 @@ window.CodeMirror = (function() { state: state}; }, - getStateAfter: function(line) { + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; + else if (styles[mid * 2 + 1] < ch) before = mid + 1; + else return styles[mid * 2 + 2]; + } + }, + + getStateAfter: function(line, precise) { var doc = this.doc; line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); - return getStateBefore(this, line + 1); + return getStateBefore(this, line + 1, precise); }, cursorCoords: function(start, mode) { @@ -2829,6 +2891,19 @@ window.CodeMirror = (function() { return coordsChar(this, coords.left, coords.top); }, + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset); + }, + heightAtLine: function(line, mode) { + var end = false, last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) line = this.doc.first; + else if (line > last) { line = last; end = true; } + var lineObj = getLine(this.doc, line); + return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top + + (end ? lineObj.height : 0); + }, + defaultTextHeight: function() { return textHeight(this.display); }, defaultCharWidth: function() { return charWidth(this.display); }, @@ -2857,7 +2932,7 @@ window.CodeMirror = (function() { return changeLine(this, handle, function(line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass"; if (!line[prop]) line[prop] = cls; - else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false; + else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false; else line[prop] += " " + cls; return true; }); @@ -2870,9 +2945,10 @@ window.CodeMirror = (function() { if (!cur) return false; else if (cls == null) line[prop] = null; else { - var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), ""); - if (upd == cur) return false; - line[prop] = upd || null; + var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)")); + if (!found) return false; + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; } return true; }); @@ -2920,7 +2996,7 @@ window.CodeMirror = (function() { if (left + node.offsetWidth > hspace) left = hspace - node.offsetWidth; } - node.style.top = (top + paddingTop(display)) + "px"; + node.style.top = top + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { left = display.sizer.clientWidth - node.offsetWidth; @@ -2988,7 +3064,8 @@ window.CodeMirror = (function() { sel.goalColumn = pos.left; }), - toggleOverwrite: function() { + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) return; if (this.state.overwrite = !this.state.overwrite) this.display.cursor.className += " CodeMirror-overwrite"; else @@ -3111,6 +3188,7 @@ window.CodeMirror = (function() { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; cm.refresh(); }, true); + option("coverGutterNextToScrollbar", false, updateScrollbars, true); option("lineNumbers", false, function(cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); @@ -3126,6 +3204,7 @@ window.CodeMirror = (function() { option("dragDrop", true); option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); option("cursorHeight", 1); option("workTime", 100); option("workDelay", 100); @@ -3163,10 +3242,15 @@ window.CodeMirror = (function() { }; CodeMirror.resolveMode = function(spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return CodeMirror.resolveMode("application/xml"); + } if (typeof spec == "string") return {name: spec}; else return spec || {name: "null"}; }; @@ -3257,6 +3341,10 @@ window.CodeMirror = (function() { var l = cm.getCursor().line; cm.replaceRange("", Pos(l, 0), Pos(l), "+delete"); }, + delLineLeft: function(cm) { + var cur = cm.getCursor(); + cm.replaceRange("", Pos(cur.line, 0), cur, "+delete"); + }, undo: function(cm) {cm.undo();}, redo: function(cm) {cm.redo();}, goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, @@ -3352,7 +3440,7 @@ window.CodeMirror = (function() { "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", - "Cmd-[": "indentLess", "Cmd-]": "indentMore", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft", fallthrough: ["basic", "emacsy"] }; keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; @@ -3391,7 +3479,7 @@ window.CodeMirror = (function() { for (var i = 0; i < maps.length; ++i) { var done = lookup(maps[i]); - if (done) return done; + if (done) return done != "stop"; } } function isModifierKey(event) { @@ -3573,7 +3661,7 @@ window.CodeMirror = (function() { if (min != null && cm) regChange(cm, min, max + 1); this.lines.length = 0; this.explicitlyCleared = true; - if (this.collapsed && this.doc.cantEdit) { + if (this.atomic && this.doc.cantEdit) { this.doc.cantEdit = false; if (cm) reCheckSelection(cm); } @@ -3596,15 +3684,18 @@ window.CodeMirror = (function() { return from && {from: from, to: to}; }; - TextMarker.prototype.getOptions = function(copyWidget) { - var repl = this.replacedWith; - return {className: this.className, - inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight, - atomic: this.atomic, - collapsed: this.collapsed, - replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl, - readOnly: this.readOnly, - startStyle: this.startStyle, endStyle: this.endStyle}; + TextMarker.prototype.changed = function() { + var pos = this.find(), cm = this.doc.cm; + if (!pos || !cm) return; + var line = getLine(this.doc, pos.from.line); + clearCachedMeasurement(cm, line); + if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) { + for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) { + if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight); + break; + } + runInOp(cm, function() { cm.curOp.selectionChanged = true; }); + } }; TextMarker.prototype.attachLine = function(line) { @@ -3633,6 +3724,7 @@ window.CodeMirror = (function() { if (marker.replacedWith) { marker.collapsed = true; marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true; } if (marker.collapsed) sawCollapsedSpans = true; @@ -3704,11 +3796,6 @@ window.CodeMirror = (function() { SharedTextMarker.prototype.find = function() { return this.primary.find(); }; - SharedTextMarker.prototype.getOptions = function(copyWidget) { - var inner = this.primary.getOptions(copyWidget); - inner.shared = true; - return inner; - }; function markTextShared(doc, from, to, options, type) { options = copyObj(options); @@ -3811,6 +3898,13 @@ window.CodeMirror = (function() { } } } + if (sameLine && first) { + // Make sure we didn't create any zero-length spans + for (var i = 0; i < first.length; ++i) + if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark") + first.splice(i--, 1); + if (!first.length) first = null; + } var newMarkers = [first]; if (!sameLine) { @@ -3905,6 +3999,7 @@ window.CodeMirror = (function() { sp = sps[i]; if (!sp.marker.collapsed) continue; if (sp.from == null) return true; + if (sp.marker.replacedWith) continue; if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) return true; } @@ -3918,7 +4013,7 @@ window.CodeMirror = (function() { return true; for (var sp, i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; - if (sp.marker.collapsed && sp.from == span.to && + if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) return true; } @@ -4030,7 +4125,7 @@ window.CodeMirror = (function() { function runMode(cm, text, mode, state, f) { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; - var curText = "", curStyle = null; + var curStart = 0, curStyle = null; var stream = new StringStream(text, cm.options.tabSize), style; if (text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol()) { @@ -4042,14 +4137,13 @@ window.CodeMirror = (function() { } else { style = mode.token(stream, state); } - var substr = stream.current(); + if (!flattenSpans || curStyle != style) { + if (curStart < stream.start) f(stream.start, curStyle); + curStart = stream.start; curStyle = style; + } stream.start = stream.pos; - if (!flattenSpans || curStyle != style) { - if (curText) f(curText, curStyle); - curText = substr; curStyle = style; - } else curText = curText + substr; - } - if (curText) f(curText, curStyle); + } + if (curStart < stream.pos) f(stream.pos, curStyle); } function highlightLine(cm, line, state) { @@ -4057,27 +4151,24 @@ window.CodeMirror = (function() { // mode/overlays that it is based on (for easy invalidation). var st = [cm.state.modeGen]; // Compute the base array of styles - runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);}); + runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);}); // Run overlays, adjust style array. for (var o = 0; o < cm.state.overlays.length; ++o) { - var overlay = cm.state.overlays[o], i = 1; - runMode(cm, line.text, overlay.mode, true, function(txt, style) { - var start = i, len = txt.length; + var overlay = cm.state.overlays[o], i = 1, at = 0; + runMode(cm, line.text, overlay.mode, true, function(end, style) { + var start = i; // Ensure there's a token end at the current position, and that i points at it - while (len) { - var cur = st[i], len_ = cur.length; - if (len_ <= len) { - len -= len_; - } else { - st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len)); - len = 0; - } + while (at < end) { + var i_end = st[i]; + if (i_end > end) + st.splice(i, 1, end, st[i+1], i_end); i += 2; + at = Math.min(end, i_end); } if (!style) return; if (overlay.opaque) { - st.splice(start, i - start, txt, style); + st.splice(start, i - start, end, style); i = start + 2; } else { for (; start < i; start += 2) { @@ -4117,37 +4208,31 @@ window.CodeMirror = (function() { } function lineContent(cm, realLine, measure) { - var merged, line = realLine, lineBefore, sawBefore, simple = true; - while (merged = collapsedSpanAtStart(line)) { - simple = false; + var merged, line = realLine, empty = true; + while (merged = collapsedSpanAtStart(line)) line = getLine(cm.doc, merged.find().from.line); - if (!lineBefore) lineBefore = line; - } var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure, - measure: null, addedOne: false, cm: cm}; + measure: null, measuredSomething: false, cm: cm}; if (line.textClass) builder.pre.className = line.textClass; do { + if (line.text) empty = false; builder.measure = line == realLine && measure; builder.pos = 0; builder.addToken = builder.measure ? buildTokenMeasure : buildToken; if ((ie || webkit) && cm.getOption("lineWrapping")) builder.addToken = buildTokenSplitSpaces(builder.addToken); - if (measure && sawBefore && line != realLine && !builder.addedOne) { + var next = insertLineContent(line, builder, getLineStyles(cm, line)); + if (measure && line == realLine && !builder.measuredSomething) { measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure)); - builder.addedOne = true; + builder.measuredSomething = true; } - var next = insertLineContent(line, builder, getLineStyles(cm, line)); - sawBefore = line == lineBefore; - if (next) { - line = getLine(cm.doc, next.to.line); - simple = false; - } + if (next) line = getLine(cm.doc, next.to.line); } while (next); - if (measure && !builder.addedOne) - measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure)); + if (measure && !builder.measuredSomething && !measure[0]) + measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure)); if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine)) builder.pre.appendChild(document.createTextNode("\u00a0")); @@ -4216,8 +4301,7 @@ window.CodeMirror = (function() { if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) { ch = text.slice(i, i + 2); ++i; - } else if (i && wrapping && - spanAffectsWrapping.test(text.slice(i - 1, i + 1))) { + } else if (i && wrapping && spanAffectsWrapping(text, i)) { builder.pre.appendChild(elt("wbr")); } var span = builder.measure[builder.pos] = @@ -4231,7 +4315,7 @@ window.CodeMirror = (function() { span.style.whiteSpace = "normal"; builder.pos += ch.length; } - if (text.length) builder.addedOne = true; + if (text.length) builder.measuredSomething = true; } function buildTokenSplitSpaces(inner) { @@ -4249,11 +4333,12 @@ window.CodeMirror = (function() { function buildCollapsedSpan(builder, size, widget) { if (widget) { if (!builder.display) widget = widget.cloneNode(true); + if (builder.measure) { + builder.measure[builder.pos] = size ? widget + : builder.pre.appendChild(zeroWidthElement(builder.cm.display.measure)); + builder.measuredSomething = true; + } builder.pre.appendChild(widget); - if (builder.measure && size) { - builder.measure[builder.pos] = widget; - builder.addedOne = true; - } } builder.pos += size; } @@ -4261,15 +4346,14 @@ window.CodeMirror = (function() { // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { - var spans = line.markedSpans; + var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i = 1; i < styles.length; i+=2) - builder.addToken(builder, styles[i], styleToClass(styles[i+1])); + builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1])); return; } - var allText = line.text, len = allText.length; - var pos = 0, i = 1, text = "", style; + var len = allText.length, pos = 0, i = 1, text = "", style; var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed; for (;;) { if (nextChange == pos) { // Update current marker set @@ -4283,7 +4367,7 @@ window.CodeMirror = (function() { if (m.className) spanStyle += " " + m.className; if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle; - if (m.collapsed && (!collapsed || collapsed.marker.width < m.width)) + if (m.collapsed && (!collapsed || collapsed.marker.size < m.size)) collapsed = sp; } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; @@ -4313,7 +4397,8 @@ window.CodeMirror = (function() { pos = end; spanStartStyle = ""; } - text = styles[i++]; style = styleToClass(styles[i++]); + text = allText.slice(at, at = styles[i++]); + style = styleToClass(styles[i++]); } } } @@ -4505,6 +4590,7 @@ window.CodeMirror = (function() { this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; this.history = makeHistory(); + this.cleanGeneration = 1; this.frontier = firstLine; var start = Pos(firstLine, 0); this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null}; @@ -4516,6 +4602,7 @@ window.CodeMirror = (function() { }; Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, iter: function(from, to, op) { if (op) this.iterN(from - this.first, to - from, op); else this.iterN(this.first, this.first + this.size, from); @@ -4604,20 +4691,25 @@ window.CodeMirror = (function() { var hist = this.history; return {undo: hist.done.length, redo: hist.undone.length}; }, - clearHistory: function() {this.history = makeHistory();}, + clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);}, markClean: function() { - this.history.dirtyCounter = 0; + this.cleanGeneration = this.changeGeneration(); + }, + changeGeneration: function() { this.history.lastOp = this.history.lastOrigin = null; + return this.history.generation; }, - isClean: function () {return this.history.dirtyCounter == 0;}, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration); + }, getHistory: function() { return {done: copyHistoryArray(this.history.done), undone: copyHistoryArray(this.history.undone)}; }, setHistory: function(histData) { - var hist = this.history = makeHistory(); + var hist = this.history = makeHistory(this.history.maxGeneration); hist.done = histData.done.slice(0); hist.undone = histData.undone.slice(0); }, @@ -4847,7 +4939,7 @@ window.CodeMirror = (function() { // HISTORY - function makeHistory() { + function makeHistory(startGen) { return { // Arrays of history events. Doing something adds an event to // done and clears undo. Undoing moves events from done to @@ -4857,7 +4949,7 @@ window.CodeMirror = (function() { // event lastTime: 0, lastOp: null, lastOrigin: null, // Used by the isClean() method - dirtyCounter: 0 + generation: startGen || 1, maxGeneration: startGen || 1 }; } @@ -4901,17 +4993,13 @@ window.CodeMirror = (function() { } else { // Can not be merged, start a new event. cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation, anchorBefore: doc.sel.anchor, headBefore: doc.sel.head, anchorAfter: selAfter.anchor, headAfter: selAfter.head}; hist.done.push(cur); + hist.generation = ++hist.maxGeneration; while (hist.done.length > hist.undoDepth) hist.done.shift(); - if (hist.dirtyCounter < 0) - // The user has made a change after undoing past the last clean state. - // We can never get back to a clean state now until markClean() is called. - hist.dirtyCounter = NaN; - else - hist.dirtyCounter++; } hist.lastTime = time; hist.lastOp = opId; @@ -5026,6 +5114,9 @@ window.CodeMirror = (function() { if (e.stopPropagation) e.stopPropagation(); else e.cancelBubble = true; } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false; + } function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} CodeMirror.e_stop = e_stop; CodeMirror.e_preventDefault = e_preventDefault; @@ -5092,6 +5183,11 @@ window.CodeMirror = (function() { delayedCallbacks.push(bnd(arr[i])); } + function signalDOMEvent(cm, e) { + signal(cm, e.type, cm, e); + return e_defaultPrevented(e); + } + function fireDelayed() { --delayedCallbackDepth; var delayed = delayedCallbacks; @@ -5146,7 +5242,11 @@ window.CodeMirror = (function() { if (ios) { // Mobile Safari apparently has a bug where select() is broken. node.selectionStart = 0; node.selectionEnd = node.value.length; - } else node.select(); + } else { + // Suppress mysterious IE10 errors + try { node.select(); } + catch(_e) {} + } } function indexOf(collection, elt) { @@ -5180,7 +5280,7 @@ window.CodeMirror = (function() { return function(){return f.apply(null, args);}; } - var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/; + var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; function isWordChar(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); @@ -5241,13 +5341,24 @@ window.CodeMirror = (function() { // word wrapping between certain characters *only* if a new inline // element is started between them. This makes it hard to reliably // measure the position of things, since that requires inserting an - // extra span. This terribly fragile set of regexps matches the + // extra span. This terribly fragile set of tests matches the // character combinations that suffer from this phenomenon on the // various browsers. - var spanAffectsWrapping = /^$/; // Won't match any two-character string - if (gecko) spanAffectsWrapping = /$'/; - else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; - else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/; + function spanAffectsWrapping() { return false; } + if (gecko) // Only for "$'" + spanAffectsWrapping = function(str, i) { + return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39; + }; + else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) + spanAffectsWrapping = function(str, i) { + return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1)); + }; + else if (webkit) + spanAffectsWrapping = function(str, i) { + if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) + return true; + return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1)); + }; var knownScrollbarWidth; function scrollbarWidth(measure) { @@ -5366,6 +5477,40 @@ window.CodeMirror = (function() { return Pos(lineN, ch); } + function compareBidiLevel(order, a, b) { + var linedir = order[0].level; + if (a == linedir) return true; + if (b == linedir) return false; + return a < b; + } + var bidiOther; + function getBidiPartAt(order, pos) { + for (var i = 0, found; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; } + if (cur.from == pos || cur.to == pos) { + if (found == null) { + found = i; + } else if (compareBidiLevel(order, cur.level, order[found].level)) { + bidiOther = found; + return i; + } else { + bidiOther = i; + return found; + } + } + } + bidiOther = null; + return found; + } + + function moveInLine(line, pos, dir, byUnit) { + if (!byUnit) return pos + dir; + do pos += dir; + while (pos > 0 && isExtendingChar.test(line.text.charAt(pos))); + return pos; + } + // This is somewhat involved. It is needed in order to move // 'visually' through bi-directional text -- i.e., pressing left // should make the cursor go left, even when in RTL text. The @@ -5375,37 +5520,24 @@ window.CodeMirror = (function() { function moveVisually(line, start, dir, byUnit) { var bidi = getOrder(line); if (!bidi) return moveLogically(line, start, dir, byUnit); - var moveOneUnit = byUnit ? function(pos, dir) { - do pos += dir; - while (pos > 0 && isExtendingChar.test(line.text.charAt(pos))); - return pos; - } : function(pos, dir) { return pos + dir; }; - var linedir = bidi[0].level; - for (var i = 0; i < bidi.length; ++i) { - var part = bidi[i], sticky = part.level % 2 == linedir; - if ((part.from < start && part.to > start) || - (sticky && (part.from == start || part.to == start))) break; - } - var target = moveOneUnit(start, part.level % 2 ? -dir : dir); - - while (target != null) { - if (part.level % 2 == linedir) { - if (target < part.from || target > part.to) { - part = bidi[i += dir]; - target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1)); - } else break; + var pos = getBidiPartAt(bidi, start), part = bidi[pos]; + var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit); + + for (;;) { + if (target > part.from && target < part.to) return target; + if (target == part.from || target == part.to) { + if (getBidiPartAt(bidi, target) == pos) return target; + part = bidi[pos += dir]; + return (dir > 0) == part.level % 2 ? part.to : part.from; } else { - if (target == bidiLeft(part)) { - part = bidi[--i]; - target = part && bidiRight(part); - } else if (target == bidiRight(part)) { - part = bidi[++i]; - target = part && bidiLeft(part); - } else break; + part = bidi[pos += dir]; + if (!part) return null; + if ((dir > 0) == part.level % 2) + target = moveInLine(line, part.to, -1, byUnit); + else + target = moveInLine(line, part.from, 1, byUnit); } } - - return target < 0 || target > line.text.length ? null : target; } function moveLogically(line, start, dir, byUnit) { @@ -5577,7 +5709,7 @@ window.CodeMirror = (function() { // THE END - CodeMirror.version = "3.12"; + CodeMirror.version = "3.14.0"; return CodeMirror; })(); diff --git a/rhodecode/public/js/mode/apl/index.html b/rhodecode/public/js/mode/apl/index.html --- a/rhodecode/public/js/mode/apl/index.html +++ b/rhodecode/public/js/mode/apl/index.html @@ -7,7 +7,7 @@ - + diff --git a/rhodecode/public/js/mode/meta.js b/rhodecode/public/js/mode/meta.js --- a/rhodecode/public/js/mode/meta.js +++ b/rhodecode/public/js/mode/meta.js @@ -16,6 +16,7 @@ CodeMirror.modeInfo = [ {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'}, {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'}, {name: 'Gas', mime: 'text/x-gas', mode: 'gas'}, + {name: 'GitHub Flavored Markdown', mode: 'gfm'}, {name: 'GO', mime: 'text/x-go', mode: 'go'}, {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'}, {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'}, @@ -74,232 +75,3 @@ CodeMirror.modeInfo = [ {name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'}, {name: 'Z80', mime: 'text/x-z80', mode: 'z80'} ]; - - -MIME_TO_EXT = { - 'application/javascript': ['*.js'], - 'text/javascript': ['*.js'], - 'application/json': ['*.json'], - 'application/postscript': ['*.ps', '*.eps'], - 'application/x-actionscript': ['*.as'], - 'application/x-actionscript3': ['*.as'], - 'application/x-awk': ['*.awk'], - 'application/x-befunge': ['*.befunge'], - 'application/x-brainfuck': ['*.bf', '*.b'], - 'application/x-cheetah': ['*.tmpl', '*.spt'], - 'application/x-coldfusion': ['*.cfm', '*.cfml', '*.cfc'], - 'application/x-csh': ['*.tcsh', '*.csh'], - 'application/x-dos-batch': ['*.bat', '*.cmd'], - 'application/x-ecl': ['*.ecl'], - 'application/x-evoque': ['*.evoque'], - 'application/x-fantom': ['*.fan'], - 'application/x-genshi': ['*.kid'], - 'application/x-gettext': ['*.pot', '*.po'], - 'application/x-jsp': ['*.jsp'], - 'application/x-mako': ['*.mao'], - 'application/x-mason': ['*.m', '*.mhtml', '*.mc', '*.mi', 'autohandler', 'dhandler'], - 'application/x-myghty': ['*.myt', 'autodelegate'], - 'application/x-php': ['*.phtml'], - 'application/x-pypylog': ['*.pypylog'], - 'application/x-qml': ['*.qml'], - 'application/x-sh': ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', '.bashrc', 'bashrc', '.bash_*', 'bash_*'], - 'application/x-sh-session': ['*.shell-session'], - 'application/x-shell-session': ['*.sh-session'], - 'application/x-smarty': ['*.tpl'], - 'application/x-ssp': ['*.ssp'], - 'application/x-troff': ['*.[1234567]', '*.man'], - 'application/x-urbiscript': ['*.u'], - 'application/xml+evoque': ['*.xml'], - 'application/xml-dtd': ['*.dtd'], - 'application/xsl+xml': ['*.xsl', '*.xslt', '*.xpl'], - 'text/S-plus': ['*.S', '*.R', '.Rhistory', '.Rprofile'], - 'text/coffeescript': ['*.coffee'], - 'text/css': ['*.css'], - 'text/haxe': ['*.hx'], - 'text/html': ['*.html', '*.htm', '*.xhtml', '*.xslt'], - 'text/html+evoque': ['*.html'], - 'text/html+ruby': ['*.rhtml'], - 'text/idl': ['*.pro'], - 'text/livescript': ['*.ls'], - 'text/matlab': ['*.m'], - 'text/octave': ['*.m'], - 'text/plain': ['*.txt'], - 'text/scilab': ['*.sci', '*.sce', '*.tst'], - 'text/smali': ['*.smali'], - 'text/x-abap': ['*.abap'], - 'text/x-ada': ['*.adb', '*.ads', '*.ada'], - 'text/x-apacheconf': ['.htaccess', 'apache.conf', 'apache2.conf'], - 'text/x-aspectj': ['*.aj'], - 'text/x-asymptote': ['*.asy'], - 'text/x-autohotkey': ['*.ahk', '*.ahkl'], - 'text/x-autoit': ['*.au3'], - 'text/x-bmx': ['*.bmx'], - 'text/x-boo': ['*.boo'], - 'text/x-c++hdr': ['*.cpp', '*.hpp', '*.c++', '*.h++', '*.cc', '*.hh', '*.cxx', '*.hxx', '*.C', '*.H', '*.cp','*.CPP'], - 'text/x-c-objdump': ['*.c-objdump'], - 'text/x-ceylon': ['*.ceylon'], - 'text/x-chdr': ['*.c', '*.h', '*.idc'], - 'text/x-clojure': ['*.clj'], - 'text/x-cmake': ['*.cmake', 'CMakeLists.txt'], - 'text/x-cobol': ['*.cob', '*.COB', '*.cpy', '*.CPY'], - 'text/x-common-lisp': ['*.cl', '*.lisp', '*.el'], - 'text/x-coq': ['*.v'], - 'text/x-cpp-objdump': ['*.cpp-objdump', '*.c++-objdump', '*.cxx-objdump'], - 'text/x-crocsrc': ['*.croc'], - 'text/x-csharp': ['*.cs'], - 'text/x-cuda': ['*.cu', '*.cuh'], - 'text/x-cython': ['*.pyx', '*.pxd', '*.pxi'], - 'text/x-d-objdump': ['*.d-objdump'], - 'text/x-dart': ['*.dart'], - 'text/x-dg': ['*.dg'], - 'text/x-diff': ['*.diff', '*.patch'], - 'text/x-dsrc': ['*.d', '*.di'], - 'text/x-duel': ['*.duel', '*.jbst'], - 'text/x-dylan': ['*.dylan', '*.dyl', '*.intr'], - 'text/x-dylan-console': ['*.dylan-console'], - 'text/x-dylan-lid': ['*.lid', '*.hdp'], - 'text/x-echdr': ['*.ec', '*.eh'], - 'text/x-elixir': ['*.ex', '*.exs'], - 'text/x-erl-shellsession': ['*.erl-sh'], - 'text/x-erlang': ['*.erl', '*.hrl', '*.es', '*.escript'], - 'text/x-factor': ['*.factor'], - 'text/x-fancysrc': ['*.fy', '*.fancypack'], - 'text/x-felix': ['*.flx', '*.flxh'], - 'text/x-fortran': ['*.f', '*.f90', '*.F', '*.F90'], - 'text/x-fsharp': ['*.fs', '*.fsi'], - 'text/x-gas': ['*.s', '*.S'], - 'text/x-gherkin': ['*.feature'], - 'text/x-glslsrc': ['*.vert', '*.frag', '*.geo'], - 'text/x-gnuplot': ['*.plot', '*.plt'], - 'text/x-gooddata-cl': ['*.gdc'], - 'text/x-gooddata-maql': ['*.maql'], - 'text/x-gosrc': ['*.go'], - 'text/x-gosu': ['*.gs', '*.gsx', '*.gsp', '*.vark'], - 'text/x-gosu-template': ['*.gst'], - 'text/x-groovy': ['*.groovy'], - 'text/x-haml': ['*.haml'], - 'text/x-haskell': ['*.hs'], - 'text/x-hybris': ['*.hy', '*.hyb'], - 'text/x-ini': ['*.ini', '*.cfg'], - 'text/x-iokesrc': ['*.ik'], - 'text/x-iosrc': ['*.io'], - 'text/x-irclog': ['*.weechatlog'], - 'text/x-jade': ['*.jade'], - 'text/x-java': ['*.java'], - 'text/x-java-properties': ['*.properties'], - 'text/x-julia': ['*.jl'], - 'text/x-kconfig': ['Kconfig', '*Config.in*', 'external.in*', 'standard-modules.in'], - 'text/x-koka': ['*.kk', '*.kki'], - 'text/x-kotlin': ['*.kt'], - 'text/x-lasso': ['*.lasso', '*.lasso[89]'], - 'text/x-literate-haskell': ['*.lhs'], - 'text/x-llvm': ['*.ll'], - 'text/x-logos': ['*.x', '*.xi', '*.xm', '*.xmi'], - 'text/x-logtalk': ['*.lgt'], - 'text/x-lua': ['*.lua', '*.wlua'], - 'text/x-makefile': ['*.mak', 'Makefile', 'makefile', 'Makefile.*', 'GNUmakefile'], - 'text/x-minidsrc': ['*.md'], - 'text/x-markdown': ['*.md'], - 'text/x-modelica': ['*.mo'], - 'text/x-modula2': ['*.def', '*.mod'], - 'text/x-monkey': ['*.monkey'], - 'text/x-moocode': ['*.moo'], - 'text/x-moonscript': ['*.moon'], - 'text/x-nasm': ['*.asm', '*.ASM'], - 'text/x-nemerle': ['*.n'], - 'text/x-newlisp': ['*.lsp', '*.nl'], - 'text/x-newspeak': ['*.ns2'], - 'text/x-nimrod': ['*.nim', '*.nimrod'], - 'text/x-nsis': ['*.nsi', '*.nsh'], - 'text/x-objdump': ['*.objdump'], - 'text/x-objective-c': ['*.m', '*.h'], - 'text/x-objective-c++': ['*.mm', '*.hh'], - 'text/x-objective-j': ['*.j'], - 'text/x-ocaml': ['*.ml', '*.mli', '*.mll', '*.mly'], - 'text/x-ooc': ['*.ooc'], - 'text/x-opa': ['*.opa'], - 'text/x-openedge': ['*.p', '*.cls'], - 'text/x-pascal': ['*.pas'], - 'text/x-perl': ['*.pl', '*.pm'], - 'text/x-php': ['*.php', '*.php[345]', '*.inc'], - 'text/x-povray': ['*.pov', '*.inc'], - 'text/x-powershell': ['*.ps1'], - 'text/x-prolog': ['*.prolog', '*.pro', '*.pl'], - 'text/x-python': ['*.py', '*.pyw', '*.sc', 'SConstruct', 'SConscript', '*.tac', '*.sage'], - 'text/x-python-traceback': ['*.pytb'], - 'text/x-python3-traceback': ['*.py3tb'], - 'text/x-r-doc': ['*.Rd'], - 'text/x-racket': ['*.rkt', '*.rktl'], - 'text/x-rebol': ['*.r', '*.r3'], - 'text/x-robotframework': ['*.txt', '*.robot'], - 'text/x-rpm-spec': ['*.spec'], - 'text/x-rst': ['*.rst', '*.rest'], - 'text/x-ruby': ['*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec', '*.rbx', '*.duby'], - 'text/x-rustsrc': ['*.rs', '*.rc'], - 'text/x-sass': ['*.sass'], - 'text/x-scala': ['*.scala'], - 'text/x-scaml': ['*.scaml'], - 'text/x-scheme': ['*.scm', '*.ss'], - 'text/x-scss': ['*.scss'], - 'text/x-smalltalk': ['*.st'], - 'text/x-snobol': ['*.snobol'], - 'text/x-sourcepawn': ['*.sp'], - 'text/x-sql': ['*.sql'], - 'text/x-sqlite3-console': ['*.sqlite3-console'], - 'text/x-squidconf': ['squid.conf'], - 'text/x-standardml': ['*.sml', '*.sig', '*.fun'], - 'text/x-systemverilog': ['*.sv', '*.svh'], - 'text/x-tcl': ['*.tcl'], - 'text/x-tea': ['*.tea'], - 'text/x-tex': ['*.tex', '*.aux', '*.toc'], - 'text/x-typescript': ['*.ts'], - 'text/x-vala': ['*.vala', '*.vapi'], - 'text/x-vbnet': ['*.vb', '*.bas'], - 'text/x-verilog': ['*.v'], - 'text/x-vhdl': ['*.vhdl', '*.vhd'], - 'text/x-vim': ['*.vim', '.vimrc', '.exrc', '.gvimrc', '_vimrc', '_exrc', '_gvimrc', 'vimrc', 'gvimrc'], - 'text/x-windows-registry': ['*.reg'], - 'text/x-xtend': ['*.xtend'], - 'text/x-yaml': ['*.yaml', '*.yml'], - 'text/xml': ['*.xml', '*.xsl', '*.rss', '*.xslt', '*.xsd', '*.wsdl'], - 'text/xquery': ['*.xqy', '*.xquery', '*.xq', '*.xql', '*.xqm'], - 'text/apl': [], - 'text/x-asterisk': [], - 'text/x-csrc': [], - 'text/x-c++src': ['*.cpp'], - 'text/x-coffeescript': ['*.coffee'], - 'text/x-d': ["*.d"], - 'text/x-ecl': ['*.ecl'], - 'text/x-go': ['*.go'], - 'text/x-haxe': ['*.hx'], - 'application/x-aspx': ['*.aspx'], - 'application/x-ejs': [], - 'message/http': [], - 'application/x-json': ['*.json'], - 'application/typescript': ['*.ts'], - 'jinja2': ['.jinja2'], - 'text/x-less': ['*.less'], - 'text/x-livescript': ['*.ls'], - 'text/mirc': [], - 'text/n-triples': [], - 'application/x-httpd-php': ['*.php'], - 'text/x-pig': [], - 'text/x-properties': ['*.properties'], - 'text/x-rsrc': [], - 'text/x-sh': ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', '.bashrc', 'bashrc', '.bash_*', 'bash_*'], - 'application/sieve': [], - 'text/x-stsrc': ['*.rs', '*.rc'], - 'text/x-smarty': ['*.tpl'], - 'application/x-sparql-query': [], - 'text/x-mariadb': ['*.sql'], - 'text/x-stex': [], - 'text/x-latex': ["*.ltx"], - 'text/x-tiddlywiki': [], - 'text/tiki': [], - 'text/x-vb': ['*.vb'], - 'text/vbscript': ['*.vb'], - 'text/velocity': [], - 'application/xml': ['*.xml', '*.xsl', '*.rss', '*.xslt', '*.xsd', '*.wsdl'], - 'application/xquery': ['*.xqy', '*.xquery', '*.xq', '*.xql', '*.xqm'], - 'text/x-z80': [], -} diff --git a/rhodecode/public/js/mode/php/index.html b/rhodecode/public/js/mode/php/index.html --- a/rhodecode/public/js/mode/php/index.html +++ b/rhodecode/public/js/mode/php/index.html @@ -43,7 +43,7 @@ function hello($who) {

Simple HTML/PHP mode based on - the C-like mode. Depends on XML, + the C-like mode. Depends on XML, JavaScript, CSS, HTMLMixed, and C-like modes.

MIME types defined: application/x-httpd-php (HTML with PHP code), text/x-php (plain, non-wrapped PHP code).