browser/devtools/sourceeditor/test/cm_test.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/sourceeditor/test/cm_test.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1878 @@
     1.4 +var Pos = CodeMirror.Pos;
     1.5 +
     1.6 +CodeMirror.defaults.rtlMoveVisually = true;
     1.7 +
     1.8 +function forEach(arr, f) {
     1.9 +  for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
    1.10 +}
    1.11 +
    1.12 +function addDoc(cm, width, height) {
    1.13 +  var content = [], line = "";
    1.14 +  for (var i = 0; i < width; ++i) line += "x";
    1.15 +  for (var i = 0; i < height; ++i) content.push(line);
    1.16 +  cm.setValue(content.join("\n"));
    1.17 +}
    1.18 +
    1.19 +function byClassName(elt, cls) {
    1.20 +  if (elt.getElementsByClassName) return elt.getElementsByClassName(cls);
    1.21 +  var found = [], re = new RegExp("\\b" + cls + "\\b");
    1.22 +  function search(elt) {
    1.23 +    if (elt.nodeType == 3) return;
    1.24 +    if (re.test(elt.className)) found.push(elt);
    1.25 +    for (var i = 0, e = elt.childNodes.length; i < e; ++i)
    1.26 +      search(elt.childNodes[i]);
    1.27 +  }
    1.28 +  search(elt);
    1.29 +  return found;
    1.30 +}
    1.31 +
    1.32 +var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
    1.33 +var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
    1.34 +var mac = /Mac/.test(navigator.platform);
    1.35 +var phantom = /PhantomJS/.test(navigator.userAgent);
    1.36 +var opera = /Opera\/\./.test(navigator.userAgent);
    1.37 +var opera_version = opera && navigator.userAgent.match(/Version\/(\d+\.\d+)/);
    1.38 +if (opera_version) opera_version = Number(opera_version);
    1.39 +var opera_lt10 = opera && (!opera_version || opera_version < 10);
    1.40 +
    1.41 +namespace = "core_";
    1.42 +
    1.43 +test("core_fromTextArea", function() {
    1.44 +  var te = document.getElementById("code");
    1.45 +  te.value = "CONTENT";
    1.46 +  var cm = CodeMirror.fromTextArea(te);
    1.47 +  is(!te.offsetHeight);
    1.48 +  eq(cm.getValue(), "CONTENT");
    1.49 +  cm.setValue("foo\nbar");
    1.50 +  eq(cm.getValue(), "foo\nbar");
    1.51 +  cm.save();
    1.52 +  is(/^foo\r?\nbar$/.test(te.value));
    1.53 +  cm.setValue("xxx");
    1.54 +  cm.toTextArea();
    1.55 +  is(te.offsetHeight);
    1.56 +  eq(te.value, "xxx");
    1.57 +});
    1.58 +
    1.59 +testCM("getRange", function(cm) {
    1.60 +  eq(cm.getLine(0), "1234");
    1.61 +  eq(cm.getLine(1), "5678");
    1.62 +  eq(cm.getLine(2), null);
    1.63 +  eq(cm.getLine(-1), null);
    1.64 +  eq(cm.getRange(Pos(0, 0), Pos(0, 3)), "123");
    1.65 +  eq(cm.getRange(Pos(0, -1), Pos(0, 200)), "1234");
    1.66 +  eq(cm.getRange(Pos(0, 2), Pos(1, 2)), "34\n56");
    1.67 +  eq(cm.getRange(Pos(1, 2), Pos(100, 0)), "78");
    1.68 +}, {value: "1234\n5678"});
    1.69 +
    1.70 +testCM("replaceRange", function(cm) {
    1.71 +  eq(cm.getValue(), "");
    1.72 +  cm.replaceRange("foo\n", Pos(0, 0));
    1.73 +  eq(cm.getValue(), "foo\n");
    1.74 +  cm.replaceRange("a\nb", Pos(0, 1));
    1.75 +  eq(cm.getValue(), "fa\nboo\n");
    1.76 +  eq(cm.lineCount(), 3);
    1.77 +  cm.replaceRange("xyzzy", Pos(0, 0), Pos(1, 1));
    1.78 +  eq(cm.getValue(), "xyzzyoo\n");
    1.79 +  cm.replaceRange("abc", Pos(0, 0), Pos(10, 0));
    1.80 +  eq(cm.getValue(), "abc");
    1.81 +  eq(cm.lineCount(), 1);
    1.82 +});
    1.83 +
    1.84 +testCM("selection", function(cm) {
    1.85 +  cm.setSelection(Pos(0, 4), Pos(2, 2));
    1.86 +  is(cm.somethingSelected());
    1.87 +  eq(cm.getSelection(), "11\n222222\n33");
    1.88 +  eqPos(cm.getCursor(false), Pos(2, 2));
    1.89 +  eqPos(cm.getCursor(true), Pos(0, 4));
    1.90 +  cm.setSelection(Pos(1, 0));
    1.91 +  is(!cm.somethingSelected());
    1.92 +  eq(cm.getSelection(), "");
    1.93 +  eqPos(cm.getCursor(true), Pos(1, 0));
    1.94 +  cm.replaceSelection("abc", "around");
    1.95 +  eq(cm.getSelection(), "abc");
    1.96 +  eq(cm.getValue(), "111111\nabc222222\n333333");
    1.97 +  cm.replaceSelection("def", "end");
    1.98 +  eq(cm.getSelection(), "");
    1.99 +  eqPos(cm.getCursor(true), Pos(1, 3));
   1.100 +  cm.setCursor(Pos(2, 1));
   1.101 +  eqPos(cm.getCursor(true), Pos(2, 1));
   1.102 +  cm.setCursor(1, 2);
   1.103 +  eqPos(cm.getCursor(true), Pos(1, 2));
   1.104 +}, {value: "111111\n222222\n333333"});
   1.105 +
   1.106 +testCM("extendSelection", function(cm) {
   1.107 +  cm.setExtending(true);
   1.108 +  addDoc(cm, 10, 10);
   1.109 +  cm.setSelection(Pos(3, 5));
   1.110 +  eqPos(cm.getCursor("head"), Pos(3, 5));
   1.111 +  eqPos(cm.getCursor("anchor"), Pos(3, 5));
   1.112 +  cm.setSelection(Pos(2, 5), Pos(5, 5));
   1.113 +  eqPos(cm.getCursor("head"), Pos(5, 5));
   1.114 +  eqPos(cm.getCursor("anchor"), Pos(2, 5));
   1.115 +  eqPos(cm.getCursor("start"), Pos(2, 5));
   1.116 +  eqPos(cm.getCursor("end"), Pos(5, 5));
   1.117 +  cm.setSelection(Pos(5, 5), Pos(2, 5));
   1.118 +  eqPos(cm.getCursor("head"), Pos(2, 5));
   1.119 +  eqPos(cm.getCursor("anchor"), Pos(5, 5));
   1.120 +  eqPos(cm.getCursor("start"), Pos(2, 5));
   1.121 +  eqPos(cm.getCursor("end"), Pos(5, 5));
   1.122 +  cm.extendSelection(Pos(3, 2));
   1.123 +  eqPos(cm.getCursor("head"), Pos(3, 2));
   1.124 +  eqPos(cm.getCursor("anchor"), Pos(5, 5));
   1.125 +  cm.extendSelection(Pos(6, 2));
   1.126 +  eqPos(cm.getCursor("head"), Pos(6, 2));
   1.127 +  eqPos(cm.getCursor("anchor"), Pos(5, 5));
   1.128 +  cm.extendSelection(Pos(6, 3), Pos(6, 4));
   1.129 +  eqPos(cm.getCursor("head"), Pos(6, 4));
   1.130 +  eqPos(cm.getCursor("anchor"), Pos(5, 5));
   1.131 +  cm.extendSelection(Pos(0, 3), Pos(0, 4));
   1.132 +  eqPos(cm.getCursor("head"), Pos(0, 3));
   1.133 +  eqPos(cm.getCursor("anchor"), Pos(5, 5));
   1.134 +  cm.extendSelection(Pos(4, 5), Pos(6, 5));
   1.135 +  eqPos(cm.getCursor("head"), Pos(6, 5));
   1.136 +  eqPos(cm.getCursor("anchor"), Pos(4, 5));
   1.137 +  cm.setExtending(false);
   1.138 +  cm.extendSelection(Pos(0, 3), Pos(0, 4));
   1.139 +  eqPos(cm.getCursor("head"), Pos(0, 3));
   1.140 +  eqPos(cm.getCursor("anchor"), Pos(0, 4));
   1.141 +});
   1.142 +
   1.143 +testCM("lines", function(cm) {
   1.144 +  eq(cm.getLine(0), "111111");
   1.145 +  eq(cm.getLine(1), "222222");
   1.146 +  eq(cm.getLine(-1), null);
   1.147 +  cm.replaceRange("", Pos(1, 0), Pos(2, 0))
   1.148 +  cm.replaceRange("abc", Pos(1, 0), Pos(1));
   1.149 +  eq(cm.getValue(), "111111\nabc");
   1.150 +}, {value: "111111\n222222\n333333"});
   1.151 +
   1.152 +testCM("indent", function(cm) {
   1.153 +  cm.indentLine(1);
   1.154 +  eq(cm.getLine(1), "   blah();");
   1.155 +  cm.setOption("indentUnit", 8);
   1.156 +  cm.indentLine(1);
   1.157 +  eq(cm.getLine(1), "\tblah();");
   1.158 +  cm.setOption("indentUnit", 10);
   1.159 +  cm.setOption("tabSize", 4);
   1.160 +  cm.indentLine(1);
   1.161 +  eq(cm.getLine(1), "\t\t  blah();");
   1.162 +}, {value: "if (x) {\nblah();\n}", indentUnit: 3, indentWithTabs: true, tabSize: 8});
   1.163 +
   1.164 +testCM("indentByNumber", function(cm) {
   1.165 +  cm.indentLine(0, 2);
   1.166 +  eq(cm.getLine(0), "  foo");
   1.167 +  cm.indentLine(0, -200);
   1.168 +  eq(cm.getLine(0), "foo");
   1.169 +  cm.setSelection(Pos(0, 0), Pos(1, 2));
   1.170 +  cm.indentSelection(3);
   1.171 +  eq(cm.getValue(), "   foo\n   bar\nbaz");
   1.172 +}, {value: "foo\nbar\nbaz"});
   1.173 +
   1.174 +test("core_defaults", function() {
   1.175 +  var defsCopy = {}, defs = CodeMirror.defaults;
   1.176 +  for (var opt in defs) defsCopy[opt] = defs[opt];
   1.177 +  defs.indentUnit = 5;
   1.178 +  defs.value = "uu";
   1.179 +  defs.indentWithTabs = true;
   1.180 +  defs.tabindex = 55;
   1.181 +  var place = document.getElementById("testground"), cm = CodeMirror(place);
   1.182 +  try {
   1.183 +    eq(cm.getOption("indentUnit"), 5);
   1.184 +    cm.setOption("indentUnit", 10);
   1.185 +    eq(defs.indentUnit, 5);
   1.186 +    eq(cm.getValue(), "uu");
   1.187 +    eq(cm.getOption("indentWithTabs"), true);
   1.188 +    eq(cm.getInputField().tabIndex, 55);
   1.189 +  }
   1.190 +  finally {
   1.191 +    for (var opt in defsCopy) defs[opt] = defsCopy[opt];
   1.192 +    place.removeChild(cm.getWrapperElement());
   1.193 +  }
   1.194 +});
   1.195 +
   1.196 +testCM("lineInfo", function(cm) {
   1.197 +  eq(cm.lineInfo(-1), null);
   1.198 +  var mark = document.createElement("span");
   1.199 +  var lh = cm.setGutterMarker(1, "FOO", mark);
   1.200 +  var info = cm.lineInfo(1);
   1.201 +  eq(info.text, "222222");
   1.202 +  eq(info.gutterMarkers.FOO, mark);
   1.203 +  eq(info.line, 1);
   1.204 +  eq(cm.lineInfo(2).gutterMarkers, null);
   1.205 +  cm.setGutterMarker(lh, "FOO", null);
   1.206 +  eq(cm.lineInfo(1).gutterMarkers, null);
   1.207 +  cm.setGutterMarker(1, "FOO", mark);
   1.208 +  cm.setGutterMarker(0, "FOO", mark);
   1.209 +  cm.clearGutter("FOO");
   1.210 +  eq(cm.lineInfo(0).gutterMarkers, null);
   1.211 +  eq(cm.lineInfo(1).gutterMarkers, null);
   1.212 +}, {value: "111111\n222222\n333333"});
   1.213 +
   1.214 +testCM("coords", function(cm) {
   1.215 +  cm.setSize(null, 100);
   1.216 +  addDoc(cm, 32, 200);
   1.217 +  var top = cm.charCoords(Pos(0, 0));
   1.218 +  var bot = cm.charCoords(Pos(200, 30));
   1.219 +  is(top.left < bot.left);
   1.220 +  is(top.top < bot.top);
   1.221 +  is(top.top < top.bottom);
   1.222 +  cm.scrollTo(null, 100);
   1.223 +  var top2 = cm.charCoords(Pos(0, 0));
   1.224 +  is(top.top > top2.top);
   1.225 +  eq(top.left, top2.left);
   1.226 +});
   1.227 +
   1.228 +testCM("coordsChar", function(cm) {
   1.229 +  addDoc(cm, 35, 70);
   1.230 +  for (var i = 0; i < 2; ++i) {
   1.231 +    var sys = i ? "local" : "page";
   1.232 +    for (var ch = 0; ch <= 35; ch += 5) {
   1.233 +      for (var line = 0; line < 70; line += 5) {
   1.234 +        cm.setCursor(line, ch);
   1.235 +        var coords = cm.charCoords(Pos(line, ch), sys);
   1.236 +        var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys);
   1.237 +        eqPos(pos, Pos(line, ch));
   1.238 +      }
   1.239 +    }
   1.240 +  }
   1.241 +}, {lineNumbers: true});
   1.242 +
   1.243 +testCM("posFromIndex", function(cm) {
   1.244 +  cm.setValue(
   1.245 +    "This function should\n" +
   1.246 +    "convert a zero based index\n" +
   1.247 +    "to line and ch."
   1.248 +  );
   1.249 +
   1.250 +  var examples = [
   1.251 +    { index: -1, line: 0, ch: 0  }, // <- Tests clipping
   1.252 +    { index: 0,  line: 0, ch: 0  },
   1.253 +    { index: 10, line: 0, ch: 10 },
   1.254 +    { index: 39, line: 1, ch: 18 },
   1.255 +    { index: 55, line: 2, ch: 7  },
   1.256 +    { index: 63, line: 2, ch: 15 },
   1.257 +    { index: 64, line: 2, ch: 15 }  // <- Tests clipping
   1.258 +  ];
   1.259 +
   1.260 +  for (var i = 0; i < examples.length; i++) {
   1.261 +    var example = examples[i];
   1.262 +    var pos = cm.posFromIndex(example.index);
   1.263 +    eq(pos.line, example.line);
   1.264 +    eq(pos.ch, example.ch);
   1.265 +    if (example.index >= 0 && example.index < 64)
   1.266 +      eq(cm.indexFromPos(pos), example.index);
   1.267 +  }
   1.268 +});
   1.269 +
   1.270 +testCM("undo", function(cm) {
   1.271 +  cm.replaceRange("def", Pos(0, 0), Pos(0));
   1.272 +  eq(cm.historySize().undo, 1);
   1.273 +  cm.undo();
   1.274 +  eq(cm.getValue(), "abc");
   1.275 +  eq(cm.historySize().undo, 0);
   1.276 +  eq(cm.historySize().redo, 1);
   1.277 +  cm.redo();
   1.278 +  eq(cm.getValue(), "def");
   1.279 +  eq(cm.historySize().undo, 1);
   1.280 +  eq(cm.historySize().redo, 0);
   1.281 +  cm.setValue("1\n\n\n2");
   1.282 +  cm.clearHistory();
   1.283 +  eq(cm.historySize().undo, 0);
   1.284 +  for (var i = 0; i < 20; ++i) {
   1.285 +    cm.replaceRange("a", Pos(0, 0));
   1.286 +    cm.replaceRange("b", Pos(3, 0));
   1.287 +  }
   1.288 +  eq(cm.historySize().undo, 40);
   1.289 +  for (var i = 0; i < 40; ++i)
   1.290 +    cm.undo();
   1.291 +  eq(cm.historySize().redo, 40);
   1.292 +  eq(cm.getValue(), "1\n\n\n2");
   1.293 +}, {value: "abc"});
   1.294 +
   1.295 +testCM("undoDepth", function(cm) {
   1.296 +  cm.replaceRange("d", Pos(0));
   1.297 +  cm.replaceRange("e", Pos(0));
   1.298 +  cm.replaceRange("f", Pos(0));
   1.299 +  cm.undo(); cm.undo(); cm.undo();
   1.300 +  eq(cm.getValue(), "abcd");
   1.301 +}, {value: "abc", undoDepth: 4});
   1.302 +
   1.303 +testCM("undoDoesntClearValue", function(cm) {
   1.304 +  cm.undo();
   1.305 +  eq(cm.getValue(), "x");
   1.306 +}, {value: "x"});
   1.307 +
   1.308 +testCM("undoMultiLine", function(cm) {
   1.309 +  cm.operation(function() {
   1.310 +    cm.replaceRange("x", Pos(0, 0));
   1.311 +    cm.replaceRange("y", Pos(1, 0));
   1.312 +  });
   1.313 +  cm.undo();
   1.314 +  eq(cm.getValue(), "abc\ndef\nghi");
   1.315 +  cm.operation(function() {
   1.316 +    cm.replaceRange("y", Pos(1, 0));
   1.317 +    cm.replaceRange("x", Pos(0, 0));
   1.318 +  });
   1.319 +  cm.undo();
   1.320 +  eq(cm.getValue(), "abc\ndef\nghi");
   1.321 +  cm.operation(function() {
   1.322 +    cm.replaceRange("y", Pos(2, 0));
   1.323 +    cm.replaceRange("x", Pos(1, 0));
   1.324 +    cm.replaceRange("z", Pos(2, 0));
   1.325 +  });
   1.326 +  cm.undo();
   1.327 +  eq(cm.getValue(), "abc\ndef\nghi", 3);
   1.328 +}, {value: "abc\ndef\nghi"});
   1.329 +
   1.330 +testCM("undoComposite", function(cm) {
   1.331 +  cm.replaceRange("y", Pos(1));
   1.332 +  cm.operation(function() {
   1.333 +    cm.replaceRange("x", Pos(0));
   1.334 +    cm.replaceRange("z", Pos(2));
   1.335 +  });
   1.336 +  eq(cm.getValue(), "ax\nby\ncz\n");
   1.337 +  cm.undo();
   1.338 +  eq(cm.getValue(), "a\nby\nc\n");
   1.339 +  cm.undo();
   1.340 +  eq(cm.getValue(), "a\nb\nc\n");
   1.341 +  cm.redo(); cm.redo();
   1.342 +  eq(cm.getValue(), "ax\nby\ncz\n");
   1.343 +}, {value: "a\nb\nc\n"});
   1.344 +
   1.345 +testCM("undoSelection", function(cm) {
   1.346 +  cm.setSelection(Pos(0, 2), Pos(0, 4));
   1.347 +  cm.replaceSelection("");
   1.348 +  cm.setCursor(Pos(1, 0));
   1.349 +  cm.undo();
   1.350 +  eqPos(cm.getCursor(true), Pos(0, 2));
   1.351 +  eqPos(cm.getCursor(false), Pos(0, 4));
   1.352 +  cm.setCursor(Pos(1, 0));
   1.353 +  cm.redo();
   1.354 +  eqPos(cm.getCursor(true), Pos(0, 2));
   1.355 +  eqPos(cm.getCursor(false), Pos(0, 2));
   1.356 +}, {value: "abcdefgh\n"});
   1.357 +
   1.358 +testCM("undoSelectionAsBefore", function(cm) {
   1.359 +  cm.replaceSelection("abc", "around");
   1.360 +  cm.undo();
   1.361 +  cm.redo();
   1.362 +  eq(cm.getSelection(), "abc");
   1.363 +});
   1.364 +
   1.365 +testCM("markTextSingleLine", function(cm) {
   1.366 +  forEach([{a: 0, b: 1, c: "", f: 2, t: 5},
   1.367 +           {a: 0, b: 4, c: "", f: 0, t: 2},
   1.368 +           {a: 1, b: 2, c: "x", f: 3, t: 6},
   1.369 +           {a: 4, b: 5, c: "", f: 3, t: 5},
   1.370 +           {a: 4, b: 5, c: "xx", f: 3, t: 7},
   1.371 +           {a: 2, b: 5, c: "", f: 2, t: 3},
   1.372 +           {a: 2, b: 5, c: "abcd", f: 6, t: 7},
   1.373 +           {a: 2, b: 6, c: "x", f: null, t: null},
   1.374 +           {a: 3, b: 6, c: "", f: null, t: null},
   1.375 +           {a: 0, b: 9, c: "hallo", f: null, t: null},
   1.376 +           {a: 4, b: 6, c: "x", f: 3, t: 4},
   1.377 +           {a: 4, b: 8, c: "", f: 3, t: 4},
   1.378 +           {a: 6, b: 6, c: "a", f: 3, t: 6},
   1.379 +           {a: 8, b: 9, c: "", f: 3, t: 6}], function(test) {
   1.380 +    cm.setValue("1234567890");
   1.381 +    var r = cm.markText(Pos(0, 3), Pos(0, 6), {className: "foo"});
   1.382 +    cm.replaceRange(test.c, Pos(0, test.a), Pos(0, test.b));
   1.383 +    var f = r.find();
   1.384 +    eq(f && f.from.ch, test.f); eq(f && f.to.ch, test.t);
   1.385 +  });
   1.386 +});
   1.387 +
   1.388 +testCM("markTextMultiLine", function(cm) {
   1.389 +  function p(v) { return v && Pos(v[0], v[1]); }
   1.390 +  forEach([{a: [0, 0], b: [0, 5], c: "", f: [0, 0], t: [2, 5]},
   1.391 +           {a: [0, 0], b: [0, 5], c: "foo\n", f: [1, 0], t: [3, 5]},
   1.392 +           {a: [0, 1], b: [0, 10], c: "", f: [0, 1], t: [2, 5]},
   1.393 +           {a: [0, 5], b: [0, 6], c: "x", f: [0, 6], t: [2, 5]},
   1.394 +           {a: [0, 0], b: [1, 0], c: "", f: [0, 0], t: [1, 5]},
   1.395 +           {a: [0, 6], b: [2, 4], c: "", f: [0, 5], t: [0, 7]},
   1.396 +           {a: [0, 6], b: [2, 4], c: "aa", f: [0, 5], t: [0, 9]},
   1.397 +           {a: [1, 2], b: [1, 8], c: "", f: [0, 5], t: [2, 5]},
   1.398 +           {a: [0, 5], b: [2, 5], c: "xx", f: null, t: null},
   1.399 +           {a: [0, 0], b: [2, 10], c: "x", f: null, t: null},
   1.400 +           {a: [1, 5], b: [2, 5], c: "", f: [0, 5], t: [1, 5]},
   1.401 +           {a: [2, 0], b: [2, 3], c: "", f: [0, 5], t: [2, 2]},
   1.402 +           {a: [2, 5], b: [3, 0], c: "a\nb", f: [0, 5], t: [2, 5]},
   1.403 +           {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 3]},
   1.404 +           {a: [1, 1], b: [1, 9], c: "1\n2\n3", f: [0, 5], t: [4, 5]}], function(test) {
   1.405 +    cm.setValue("aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\ndddddddd\n");
   1.406 +    var r = cm.markText(Pos(0, 5), Pos(2, 5),
   1.407 +                        {className: "CodeMirror-matchingbracket"});
   1.408 +    cm.replaceRange(test.c, p(test.a), p(test.b));
   1.409 +    var f = r.find();
   1.410 +    eqPos(f && f.from, p(test.f)); eqPos(f && f.to, p(test.t));
   1.411 +  });
   1.412 +});
   1.413 +
   1.414 +testCM("markTextUndo", function(cm) {
   1.415 +  var marker1, marker2, bookmark;
   1.416 +  marker1 = cm.markText(Pos(0, 1), Pos(0, 3),
   1.417 +                        {className: "CodeMirror-matchingbracket"});
   1.418 +  marker2 = cm.markText(Pos(0, 0), Pos(2, 1),
   1.419 +                        {className: "CodeMirror-matchingbracket"});
   1.420 +  bookmark = cm.setBookmark(Pos(1, 5));
   1.421 +  cm.operation(function(){
   1.422 +    cm.replaceRange("foo", Pos(0, 2));
   1.423 +    cm.replaceRange("bar\nbaz\nbug\n", Pos(2, 0), Pos(3, 0));
   1.424 +  });
   1.425 +  var v1 = cm.getValue();
   1.426 +  cm.setValue("");
   1.427 +  eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null);
   1.428 +  cm.undo();
   1.429 +  eqPos(bookmark.find(), Pos(1, 5), "still there");
   1.430 +  cm.undo();
   1.431 +  var m1Pos = marker1.find(), m2Pos = marker2.find();
   1.432 +  eqPos(m1Pos.from, Pos(0, 1)); eqPos(m1Pos.to, Pos(0, 3));
   1.433 +  eqPos(m2Pos.from, Pos(0, 0)); eqPos(m2Pos.to, Pos(2, 1));
   1.434 +  eqPos(bookmark.find(), Pos(1, 5));
   1.435 +  cm.redo(); cm.redo();
   1.436 +  eq(bookmark.find(), null);
   1.437 +  cm.undo();
   1.438 +  eqPos(bookmark.find(), Pos(1, 5));
   1.439 +  eq(cm.getValue(), v1);
   1.440 +}, {value: "1234\n56789\n00\n"});
   1.441 +
   1.442 +testCM("markTextStayGone", function(cm) {
   1.443 +  var m1 = cm.markText(Pos(0, 0), Pos(0, 1));
   1.444 +  cm.replaceRange("hi", Pos(0, 2));
   1.445 +  m1.clear();
   1.446 +  cm.undo();
   1.447 +  eq(m1.find(), null);
   1.448 +}, {value: "hello"});
   1.449 +
   1.450 +testCM("markTextAllowEmpty", function(cm) {
   1.451 +  var m1 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false});
   1.452 +  is(m1.find());
   1.453 +  cm.replaceRange("x", Pos(0, 0));
   1.454 +  is(m1.find());
   1.455 +  cm.replaceRange("y", Pos(0, 2));
   1.456 +  is(m1.find());
   1.457 +  cm.replaceRange("z", Pos(0, 3), Pos(0, 4));
   1.458 +  is(!m1.find());
   1.459 +  var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false,
   1.460 +                                              inclusiveLeft: true,
   1.461 +                                              inclusiveRight: true});
   1.462 +  cm.replaceRange("q", Pos(0, 1), Pos(0, 2));
   1.463 +  is(m2.find());
   1.464 +  cm.replaceRange("", Pos(0, 0), Pos(0, 3));
   1.465 +  is(!m2.find());
   1.466 +  var m3 = cm.markText(Pos(0, 1), Pos(0, 1), {clearWhenEmpty: false});
   1.467 +  cm.replaceRange("a", Pos(0, 3));
   1.468 +  is(m3.find());
   1.469 +  cm.replaceRange("b", Pos(0, 1));
   1.470 +  is(!m3.find());
   1.471 +}, {value: "abcde"});
   1.472 +
   1.473 +testCM("markTextStacked", function(cm) {
   1.474 +  var m1 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
   1.475 +  var m2 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
   1.476 +  cm.replaceRange("B", Pos(0, 1));
   1.477 +  is(m1.find() && m2.find());
   1.478 +}, {value: "A"});
   1.479 +
   1.480 +testCM("undoPreservesNewMarks", function(cm) {
   1.481 +  cm.markText(Pos(0, 3), Pos(0, 4));
   1.482 +  cm.markText(Pos(1, 1), Pos(1, 3));
   1.483 +  cm.replaceRange("", Pos(0, 3), Pos(3, 1));
   1.484 +  var mBefore = cm.markText(Pos(0, 0), Pos(0, 1));
   1.485 +  var mAfter = cm.markText(Pos(0, 5), Pos(0, 6));
   1.486 +  var mAround = cm.markText(Pos(0, 2), Pos(0, 4));
   1.487 +  cm.undo();
   1.488 +  eqPos(mBefore.find().from, Pos(0, 0));
   1.489 +  eqPos(mBefore.find().to, Pos(0, 1));
   1.490 +  eqPos(mAfter.find().from, Pos(3, 3));
   1.491 +  eqPos(mAfter.find().to, Pos(3, 4));
   1.492 +  eqPos(mAround.find().from, Pos(0, 2));
   1.493 +  eqPos(mAround.find().to, Pos(3, 2));
   1.494 +  var found = cm.findMarksAt(Pos(2, 2));
   1.495 +  eq(found.length, 1);
   1.496 +  eq(found[0], mAround);
   1.497 +}, {value: "aaaa\nbbbb\ncccc\ndddd"});
   1.498 +
   1.499 +testCM("markClearBetween", function(cm) {
   1.500 +  cm.setValue("aaa\nbbb\nccc\nddd\n");
   1.501 +  cm.markText(Pos(0, 0), Pos(2));
   1.502 +  cm.replaceRange("aaa\nbbb\nccc", Pos(0, 0), Pos(2));
   1.503 +  eq(cm.findMarksAt(Pos(1, 1)).length, 0);
   1.504 +});
   1.505 +
   1.506 +testCM("deleteSpanCollapsedInclusiveLeft", function(cm) {
   1.507 +  var from = Pos(1, 0), to = Pos(1, 1);
   1.508 +  var m = cm.markText(from, to, {collapsed: true, inclusiveLeft: true});
   1.509 +  // Delete collapsed span.
   1.510 +  cm.replaceRange("", from, to);
   1.511 +}, {value: "abc\nX\ndef"});
   1.512 +
   1.513 +testCM("bookmark", function(cm) {
   1.514 +  function p(v) { return v && Pos(v[0], v[1]); }
   1.515 +  forEach([{a: [1, 0], b: [1, 1], c: "", d: [1, 4]},
   1.516 +           {a: [1, 1], b: [1, 1], c: "xx", d: [1, 7]},
   1.517 +           {a: [1, 4], b: [1, 5], c: "ab", d: [1, 6]},
   1.518 +           {a: [1, 4], b: [1, 6], c: "", d: null},
   1.519 +           {a: [1, 5], b: [1, 6], c: "abc", d: [1, 5]},
   1.520 +           {a: [1, 6], b: [1, 8], c: "", d: [1, 5]},
   1.521 +           {a: [1, 4], b: [1, 4], c: "\n\n", d: [3, 1]},
   1.522 +           {bm: [1, 9], a: [1, 1], b: [1, 1], c: "\n", d: [2, 8]}], function(test) {
   1.523 +    cm.setValue("1234567890\n1234567890\n1234567890");
   1.524 +    var b = cm.setBookmark(p(test.bm) || Pos(1, 5));
   1.525 +    cm.replaceRange(test.c, p(test.a), p(test.b));
   1.526 +    eqPos(b.find(), p(test.d));
   1.527 +  });
   1.528 +});
   1.529 +
   1.530 +testCM("bookmarkInsertLeft", function(cm) {
   1.531 +  var br = cm.setBookmark(Pos(0, 2), {insertLeft: false});
   1.532 +  var bl = cm.setBookmark(Pos(0, 2), {insertLeft: true});
   1.533 +  cm.setCursor(Pos(0, 2));
   1.534 +  cm.replaceSelection("hi");
   1.535 +  eqPos(br.find(), Pos(0, 2));
   1.536 +  eqPos(bl.find(), Pos(0, 4));
   1.537 +  cm.replaceRange("", Pos(0, 4), Pos(0, 5));
   1.538 +  cm.replaceRange("", Pos(0, 2), Pos(0, 4));
   1.539 +  cm.replaceRange("", Pos(0, 1), Pos(0, 2));
   1.540 +  // Verify that deleting next to bookmarks doesn't kill them
   1.541 +  eqPos(br.find(), Pos(0, 1));
   1.542 +  eqPos(bl.find(), Pos(0, 1));
   1.543 +}, {value: "abcdef"});
   1.544 +
   1.545 +testCM("bookmarkCursor", function(cm) {
   1.546 +  var pos01 = cm.cursorCoords(Pos(0, 1)), pos11 = cm.cursorCoords(Pos(1, 1)),
   1.547 +      pos20 = cm.cursorCoords(Pos(2, 0)), pos30 = cm.cursorCoords(Pos(3, 0)),
   1.548 +      pos41 = cm.cursorCoords(Pos(4, 1));
   1.549 +  cm.setBookmark(Pos(0, 1), {widget: document.createTextNode("←"), insertLeft: true});
   1.550 +  cm.setBookmark(Pos(2, 0), {widget: document.createTextNode("←"), insertLeft: true});
   1.551 +  cm.setBookmark(Pos(1, 1), {widget: document.createTextNode("→")});
   1.552 +  cm.setBookmark(Pos(3, 0), {widget: document.createTextNode("→")});
   1.553 +  var new01 = cm.cursorCoords(Pos(0, 1)), new11 = cm.cursorCoords(Pos(1, 1)),
   1.554 +      new20 = cm.cursorCoords(Pos(2, 0)), new30 = cm.cursorCoords(Pos(3, 0));
   1.555 +  near(new01.left, pos01.left, 1);
   1.556 +  near(new01.top, pos01.top, 1);
   1.557 +  is(new11.left > pos11.left, "at right, middle of line");
   1.558 +  near(new11.top == pos11.top, 1);
   1.559 +  near(new20.left, pos20.left, 1);
   1.560 +  near(new20.top, pos20.top, 1);
   1.561 +  is(new30.left > pos30.left, "at right, empty line");
   1.562 +  near(new30.top, pos30, 1);
   1.563 +  cm.setBookmark(Pos(4, 0), {widget: document.createTextNode("→")});
   1.564 +  is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug");
   1.565 +}, {value: "foo\nbar\n\n\nx\ny"});
   1.566 +
   1.567 +testCM("multiBookmarkCursor", function(cm) {
   1.568 +  if (phantom) return;
   1.569 +  var ms = [], m;
   1.570 +  function add(insertLeft) {
   1.571 +    for (var i = 0; i < 3; ++i) {
   1.572 +      var node = document.createElement("span");
   1.573 +      node.innerHTML = "X";
   1.574 +      ms.push(cm.setBookmark(Pos(0, 1), {widget: node, insertLeft: insertLeft}));
   1.575 +    }
   1.576 +  }
   1.577 +  var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left;
   1.578 +  add(true);
   1.579 +  near(base1, cm.cursorCoords(Pos(0, 1)).left, 1);
   1.580 +  while (m = ms.pop()) m.clear();
   1.581 +  add(false);
   1.582 +  near(base4, cm.cursorCoords(Pos(0, 1)).left, 1);
   1.583 +}, {value: "abcdefg"});
   1.584 +
   1.585 +testCM("getAllMarks", function(cm) {
   1.586 +  addDoc(cm, 10, 10);
   1.587 +  var m1 = cm.setBookmark(Pos(0, 2));
   1.588 +  var m2 = cm.markText(Pos(0, 2), Pos(3, 2));
   1.589 +  var m3 = cm.markText(Pos(1, 2), Pos(1, 8));
   1.590 +  var m4 = cm.markText(Pos(8, 0), Pos(9, 0));
   1.591 +  eq(cm.getAllMarks().length, 4);
   1.592 +  m1.clear();
   1.593 +  m3.clear();
   1.594 +  eq(cm.getAllMarks().length, 2);
   1.595 +});
   1.596 +
   1.597 +testCM("bug577", function(cm) {
   1.598 +  cm.setValue("a\nb");
   1.599 +  cm.clearHistory();
   1.600 +  cm.setValue("fooooo");
   1.601 +  cm.undo();
   1.602 +});
   1.603 +
   1.604 +testCM("scrollSnap", function(cm) {
   1.605 +  cm.setSize(100, 100);
   1.606 +  addDoc(cm, 200, 200);
   1.607 +  cm.setCursor(Pos(100, 180));
   1.608 +  var info = cm.getScrollInfo();
   1.609 +  is(info.left > 0 && info.top > 0);
   1.610 +  cm.setCursor(Pos(0, 0));
   1.611 +  info = cm.getScrollInfo();
   1.612 +  is(info.left == 0 && info.top == 0, "scrolled clean to top");
   1.613 +  cm.setCursor(Pos(100, 180));
   1.614 +  cm.setCursor(Pos(199, 0));
   1.615 +  info = cm.getScrollInfo();
   1.616 +  is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom");
   1.617 +});
   1.618 +
   1.619 +testCM("scrollIntoView", function(cm) {
   1.620 +  if (phantom) return;
   1.621 +  var outer = cm.getWrapperElement().getBoundingClientRect();
   1.622 +  function test(line, ch, msg) {
   1.623 +    var pos = Pos(line, ch);
   1.624 +    cm.scrollIntoView(pos);
   1.625 +    var box = cm.charCoords(pos, "window");
   1.626 +    is(box.left >= outer.left, msg + " (left)");
   1.627 +    is(box.right <= outer.right, msg + " (right)");
   1.628 +    is(box.top >= outer.top, msg + " (top)");
   1.629 +    is(box.bottom <= outer.bottom, msg + " (bottom)");
   1.630 +  }
   1.631 +  addDoc(cm, 200, 200);
   1.632 +  test(199, 199, "bottom right");
   1.633 +  test(0, 0, "top left");
   1.634 +  test(100, 100, "center");
   1.635 +  test(199, 0, "bottom left");
   1.636 +  test(0, 199, "top right");
   1.637 +  test(100, 100, "center again");
   1.638 +});
   1.639 +
   1.640 +testCM("scrollBackAndForth", function(cm) {
   1.641 +  addDoc(cm, 1, 200);
   1.642 +  cm.operation(function() {
   1.643 +    cm.scrollIntoView(Pos(199, 0));
   1.644 +    cm.scrollIntoView(Pos(4, 0));
   1.645 +  });
   1.646 +  is(cm.getScrollInfo().top > 0);
   1.647 +});
   1.648 +
   1.649 +testCM("selectAllNoScroll", function(cm) {
   1.650 +  addDoc(cm, 1, 200);
   1.651 +  cm.execCommand("selectAll");
   1.652 +  eq(cm.getScrollInfo().top, 0);
   1.653 +  cm.setCursor(199);
   1.654 +  cm.execCommand("selectAll");
   1.655 +  is(cm.getScrollInfo().top > 0);
   1.656 +});
   1.657 +
   1.658 +testCM("selectionPos", function(cm) {
   1.659 +  if (phantom) return;
   1.660 +  cm.setSize(100, 100);
   1.661 +  addDoc(cm, 200, 100);
   1.662 +  cm.setSelection(Pos(1, 100), Pos(98, 100));
   1.663 +  var lineWidth = cm.charCoords(Pos(0, 200), "local").left;
   1.664 +  var lineHeight = (cm.charCoords(Pos(99)).top - cm.charCoords(Pos(0)).top) / 100;
   1.665 +  cm.scrollTo(0, 0);
   1.666 +  var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected");
   1.667 +  var outer = cm.getWrapperElement().getBoundingClientRect();
   1.668 +  var sawMiddle, sawTop, sawBottom;
   1.669 +  for (var i = 0, e = selElt.length; i < e; ++i) {
   1.670 +    var box = selElt[i].getBoundingClientRect();
   1.671 +    var atLeft = box.left - outer.left < 30;
   1.672 +    var width = box.right - box.left;
   1.673 +    var atRight = box.right - outer.left > .8 * lineWidth;
   1.674 +    if (atLeft && atRight) {
   1.675 +      sawMiddle = true;
   1.676 +      is(box.bottom - box.top > 90 * lineHeight, "middle high");
   1.677 +      is(width > .9 * lineWidth, "middle wide");
   1.678 +    } else {
   1.679 +      is(width > .4 * lineWidth, "top/bot wide enough");
   1.680 +      is(width < .6 * lineWidth, "top/bot slim enough");
   1.681 +      if (atLeft) {
   1.682 +        sawBottom = true;
   1.683 +        is(box.top - outer.top > 96 * lineHeight, "bot below");
   1.684 +      } else if (atRight) {
   1.685 +        sawTop = true;
   1.686 +        is(box.top - outer.top < 2.1 * lineHeight, "top above");
   1.687 +      }
   1.688 +    }
   1.689 +  }
   1.690 +  is(sawTop && sawBottom && sawMiddle, "all parts");
   1.691 +}, null);
   1.692 +
   1.693 +testCM("restoreHistory", function(cm) {
   1.694 +  cm.setValue("abc\ndef");
   1.695 +  cm.replaceRange("hello", Pos(1, 0), Pos(1));
   1.696 +  cm.replaceRange("goop", Pos(0, 0), Pos(0));
   1.697 +  cm.undo();
   1.698 +  var storedVal = cm.getValue(), storedHist = cm.getHistory();
   1.699 +  if (window.JSON) storedHist = JSON.parse(JSON.stringify(storedHist));
   1.700 +  eq(storedVal, "abc\nhello");
   1.701 +  cm.setValue("");
   1.702 +  cm.clearHistory();
   1.703 +  eq(cm.historySize().undo, 0);
   1.704 +  cm.setValue(storedVal);
   1.705 +  cm.setHistory(storedHist);
   1.706 +  cm.redo();
   1.707 +  eq(cm.getValue(), "goop\nhello");
   1.708 +  cm.undo(); cm.undo();
   1.709 +  eq(cm.getValue(), "abc\ndef");
   1.710 +});
   1.711 +
   1.712 +testCM("doubleScrollbar", function(cm) {
   1.713 +  var dummy = document.body.appendChild(document.createElement("p"));
   1.714 +  dummy.style.cssText = "height: 50px; overflow: scroll; width: 50px";
   1.715 +  var scrollbarWidth = dummy.offsetWidth + 1 - dummy.clientWidth;
   1.716 +  document.body.removeChild(dummy);
   1.717 +  if (scrollbarWidth < 2) return;
   1.718 +  cm.setSize(null, 100);
   1.719 +  addDoc(cm, 1, 300);
   1.720 +  var wrap = cm.getWrapperElement();
   1.721 +  is(wrap.offsetWidth - byClassName(wrap, "CodeMirror-lines")[0].offsetWidth <= scrollbarWidth * 1.5);
   1.722 +});
   1.723 +
   1.724 +testCM("weirdLinebreaks", function(cm) {
   1.725 +  cm.setValue("foo\nbar\rbaz\r\nquux\n\rplop");
   1.726 +  is(cm.getValue(), "foo\nbar\nbaz\nquux\n\nplop");
   1.727 +  is(cm.lineCount(), 6);
   1.728 +  cm.setValue("\n\n");
   1.729 +  is(cm.lineCount(), 3);
   1.730 +});
   1.731 +
   1.732 +testCM("setSize", function(cm) {
   1.733 +  cm.setSize(100, 100);
   1.734 +  var wrap = cm.getWrapperElement();
   1.735 +  is(wrap.offsetWidth, 100);
   1.736 +  is(wrap.offsetHeight, 100);
   1.737 +  cm.setSize("100%", "3em");
   1.738 +  is(wrap.style.width, "100%");
   1.739 +  is(wrap.style.height, "3em");
   1.740 +  cm.setSize(null, 40);
   1.741 +  is(wrap.style.width, "100%");
   1.742 +  is(wrap.style.height, "40px");
   1.743 +});
   1.744 +
   1.745 +function foldLines(cm, start, end, autoClear) {
   1.746 +  return cm.markText(Pos(start, 0), Pos(end - 1), {
   1.747 +    inclusiveLeft: true,
   1.748 +    inclusiveRight: true,
   1.749 +    collapsed: true,
   1.750 +    clearOnEnter: autoClear
   1.751 +  });
   1.752 +}
   1.753 +
   1.754 +testCM("collapsedLines", function(cm) {
   1.755 +  addDoc(cm, 4, 10);
   1.756 +  var range = foldLines(cm, 4, 5), cleared = 0;
   1.757 +  CodeMirror.on(range, "clear", function() {cleared++;});
   1.758 +  cm.setCursor(Pos(3, 0));
   1.759 +  CodeMirror.commands.goLineDown(cm);
   1.760 +  eqPos(cm.getCursor(), Pos(5, 0));
   1.761 +  cm.replaceRange("abcdefg", Pos(3, 0), Pos(3));
   1.762 +  cm.setCursor(Pos(3, 6));
   1.763 +  CodeMirror.commands.goLineDown(cm);
   1.764 +  eqPos(cm.getCursor(), Pos(5, 4));
   1.765 +  cm.replaceRange("ab", Pos(3, 0), Pos(3));
   1.766 +  cm.setCursor(Pos(3, 2));
   1.767 +  CodeMirror.commands.goLineDown(cm);
   1.768 +  eqPos(cm.getCursor(), Pos(5, 2));
   1.769 +  cm.operation(function() {range.clear(); range.clear();});
   1.770 +  eq(cleared, 1);
   1.771 +});
   1.772 +
   1.773 +testCM("collapsedRangeCoordsChar", function(cm) {
   1.774 +  var pos_1_3 = cm.charCoords(Pos(1, 3));
   1.775 +  pos_1_3.left += 2; pos_1_3.top += 2;
   1.776 +  var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true};
   1.777 +  var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts);
   1.778 +  eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
   1.779 +  m1.clear();
   1.780 +  var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true});
   1.781 +  var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true});
   1.782 +  eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
   1.783 +  m1.clear(); m2.clear();
   1.784 +  var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts);
   1.785 +  eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
   1.786 +}, {value: "123456\nabcdef\nghijkl\nmnopqr\n"});
   1.787 +
   1.788 +testCM("collapsedRangeBetweenLinesSelected", function(cm) {
   1.789 +  var widget = document.createElement("span");
   1.790 +  widget.textContent = "\u2194";
   1.791 +  cm.markText(Pos(0, 3), Pos(1, 0), {replacedWith: widget});
   1.792 +  cm.setSelection(Pos(0, 3), Pos(1, 0));
   1.793 +  var selElts = byClassName(cm.getWrapperElement(), "CodeMirror-selected");
   1.794 +  for (var i = 0, w = 0; i < selElts.length; i++)
   1.795 +    w += selElts[i].offsetWidth;
   1.796 +  is(w > 0);
   1.797 +}, {value: "one\ntwo"});
   1.798 +
   1.799 +testCM("randomCollapsedRanges", function(cm) {
   1.800 +  addDoc(cm, 20, 500);
   1.801 +  cm.operation(function() {
   1.802 +    for (var i = 0; i < 200; i++) {
   1.803 +      var start = Pos(Math.floor(Math.random() * 500), Math.floor(Math.random() * 20));
   1.804 +      if (i % 4)
   1.805 +        try { cm.markText(start, Pos(start.line + 2, 1), {collapsed: true}); }
   1.806 +        catch(e) { if (!/overlapping/.test(String(e))) throw e; }
   1.807 +      else
   1.808 +        cm.markText(start, Pos(start.line, start.ch + 4), {"className": "foo"});
   1.809 +    }
   1.810 +  });
   1.811 +});
   1.812 +
   1.813 +testCM("hiddenLinesAutoUnfold", function(cm) {
   1.814 +  var range = foldLines(cm, 1, 3, true), cleared = 0;
   1.815 +  CodeMirror.on(range, "clear", function() {cleared++;});
   1.816 +  cm.setCursor(Pos(3, 0));
   1.817 +  eq(cleared, 0);
   1.818 +  cm.execCommand("goCharLeft");
   1.819 +  eq(cleared, 1);
   1.820 +  range = foldLines(cm, 1, 3, true);
   1.821 +  CodeMirror.on(range, "clear", function() {cleared++;});
   1.822 +  eqPos(cm.getCursor(), Pos(3, 0));
   1.823 +  cm.setCursor(Pos(0, 3));
   1.824 +  cm.execCommand("goCharRight");
   1.825 +  eq(cleared, 2);
   1.826 +}, {value: "abc\ndef\nghi\njkl"});
   1.827 +
   1.828 +testCM("hiddenLinesSelectAll", function(cm) {  // Issue #484
   1.829 +  addDoc(cm, 4, 20);
   1.830 +  foldLines(cm, 0, 10);
   1.831 +  foldLines(cm, 11, 20);
   1.832 +  CodeMirror.commands.selectAll(cm);
   1.833 +  eqPos(cm.getCursor(true), Pos(10, 0));
   1.834 +  eqPos(cm.getCursor(false), Pos(10, 4));
   1.835 +});
   1.836 +
   1.837 +
   1.838 +testCM("everythingFolded", function(cm) {
   1.839 +  addDoc(cm, 2, 2);
   1.840 +  function enterPress() {
   1.841 +    cm.triggerOnKeyDown({type: "keydown", keyCode: 13, preventDefault: function(){}, stopPropagation: function(){}});
   1.842 +  }
   1.843 +  var fold = foldLines(cm, 0, 2);
   1.844 +  enterPress();
   1.845 +  eq(cm.getValue(), "xx\nxx");
   1.846 +  fold.clear();
   1.847 +  fold = foldLines(cm, 0, 2, true);
   1.848 +  eq(fold.find(), null);
   1.849 +  enterPress();
   1.850 +  eq(cm.getValue(), "\nxx\nxx");
   1.851 +});
   1.852 +
   1.853 +testCM("structuredFold", function(cm) {
   1.854 +  if (phantom) return;
   1.855 +  addDoc(cm, 4, 8);
   1.856 +  var range = cm.markText(Pos(1, 2), Pos(6, 2), {
   1.857 +    replacedWith: document.createTextNode("Q")
   1.858 +  });
   1.859 +  cm.setCursor(0, 3);
   1.860 +  CodeMirror.commands.goLineDown(cm);
   1.861 +  eqPos(cm.getCursor(), Pos(6, 2));
   1.862 +  CodeMirror.commands.goCharLeft(cm);
   1.863 +  eqPos(cm.getCursor(), Pos(1, 2));
   1.864 +  CodeMirror.commands.delCharAfter(cm);
   1.865 +  eq(cm.getValue(), "xxxx\nxxxx\nxxxx");
   1.866 +  addDoc(cm, 4, 8);
   1.867 +  range = cm.markText(Pos(1, 2), Pos(6, 2), {
   1.868 +    replacedWith: document.createTextNode("M"),
   1.869 +    clearOnEnter: true
   1.870 +  });
   1.871 +  var cleared = 0;
   1.872 +  CodeMirror.on(range, "clear", function(){++cleared;});
   1.873 +  cm.setCursor(0, 3);
   1.874 +  CodeMirror.commands.goLineDown(cm);
   1.875 +  eqPos(cm.getCursor(), Pos(6, 2));
   1.876 +  CodeMirror.commands.goCharLeft(cm);
   1.877 +  eqPos(cm.getCursor(), Pos(6, 1));
   1.878 +  eq(cleared, 1);
   1.879 +  range.clear();
   1.880 +  eq(cleared, 1);
   1.881 +  range = cm.markText(Pos(1, 2), Pos(6, 2), {
   1.882 +    replacedWith: document.createTextNode("Q"),
   1.883 +    clearOnEnter: true
   1.884 +  });
   1.885 +  range.clear();
   1.886 +  cm.setCursor(1, 2);
   1.887 +  CodeMirror.commands.goCharRight(cm);
   1.888 +  eqPos(cm.getCursor(), Pos(1, 3));
   1.889 +  range = cm.markText(Pos(2, 0), Pos(4, 4), {
   1.890 +    replacedWith: document.createTextNode("M")
   1.891 +  });
   1.892 +  cm.setCursor(1, 0);
   1.893 +  CodeMirror.commands.goLineDown(cm);
   1.894 +  eqPos(cm.getCursor(), Pos(2, 0));
   1.895 +}, null);
   1.896 +
   1.897 +testCM("nestedFold", function(cm) {
   1.898 +  addDoc(cm, 10, 3);
   1.899 +  function fold(ll, cl, lr, cr) {
   1.900 +    return cm.markText(Pos(ll, cl), Pos(lr, cr), {collapsed: true});
   1.901 +  }
   1.902 +  var inner1 = fold(0, 6, 1, 3), inner2 = fold(0, 2, 1, 8), outer = fold(0, 1, 2, 3), inner0 = fold(0, 5, 0, 6);
   1.903 +  cm.setCursor(0, 1);
   1.904 +  CodeMirror.commands.goCharRight(cm);
   1.905 +  eqPos(cm.getCursor(), Pos(2, 3));
   1.906 +  inner0.clear();
   1.907 +  CodeMirror.commands.goCharLeft(cm);
   1.908 +  eqPos(cm.getCursor(), Pos(0, 1));
   1.909 +  outer.clear();
   1.910 +  CodeMirror.commands.goCharRight(cm);
   1.911 +  eqPos(cm.getCursor(), Pos(0, 2));
   1.912 +  CodeMirror.commands.goCharRight(cm);
   1.913 +  eqPos(cm.getCursor(), Pos(1, 8));
   1.914 +  inner2.clear();
   1.915 +  CodeMirror.commands.goCharLeft(cm);
   1.916 +  eqPos(cm.getCursor(), Pos(1, 7));
   1.917 +  cm.setCursor(0, 5);
   1.918 +  CodeMirror.commands.goCharRight(cm);
   1.919 +  eqPos(cm.getCursor(), Pos(0, 6));
   1.920 +  CodeMirror.commands.goCharRight(cm);
   1.921 +  eqPos(cm.getCursor(), Pos(1, 3));
   1.922 +});
   1.923 +
   1.924 +testCM("badNestedFold", function(cm) {
   1.925 +  addDoc(cm, 4, 4);
   1.926 +  cm.markText(Pos(0, 2), Pos(3, 2), {collapsed: true});
   1.927 +  var caught;
   1.928 +  try {cm.markText(Pos(0, 1), Pos(0, 3), {collapsed: true});}
   1.929 +  catch(e) {caught = e;}
   1.930 +  is(caught instanceof Error, "no error");
   1.931 +  is(/overlap/i.test(caught.message), "wrong error");
   1.932 +});
   1.933 +
   1.934 +testCM("nestedFoldOnSide", function(cm) {
   1.935 +  var m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true, inclusiveRight: true});
   1.936 +  var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true});
   1.937 +  cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}).clear();
   1.938 +  try { cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true, inclusiveLeft: true}); }
   1.939 +  catch(e) { var caught = e; }
   1.940 +  is(caught && /overlap/i.test(caught.message));
   1.941 +  var m3 = cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true});
   1.942 +  var m4 = cm.markText(Pos(2, 0), Pos(2, 1), {collapse: true, inclusiveRight: true});
   1.943 +  m1.clear(); m4.clear();
   1.944 +  m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true});
   1.945 +  cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}).clear();
   1.946 +  try { cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true, inclusiveRight: true}); }
   1.947 +  catch(e) { var caught = e; }
   1.948 +  is(caught && /overlap/i.test(caught.message));
   1.949 +}, {value: "ab\ncd\ef"});
   1.950 +
   1.951 +testCM("editInFold", function(cm) {
   1.952 +  addDoc(cm, 4, 6);
   1.953 +  var m = cm.markText(Pos(1, 2), Pos(3, 2), {collapsed: true});
   1.954 +  cm.replaceRange("", Pos(0, 0), Pos(1, 3));
   1.955 +  cm.replaceRange("", Pos(2, 1), Pos(3, 3));
   1.956 +  cm.replaceRange("a\nb\nc\nd", Pos(0, 1), Pos(1, 0));
   1.957 +  cm.cursorCoords(Pos(0, 0));
   1.958 +});
   1.959 +
   1.960 +testCM("wrappingInlineWidget", function(cm) {
   1.961 +  cm.setSize("11em");
   1.962 +  var w = document.createElement("span");
   1.963 +  w.style.color = "red";
   1.964 +  w.innerHTML = "one two three four";
   1.965 +  cm.markText(Pos(0, 6), Pos(0, 9), {replacedWith: w});
   1.966 +  var cur0 = cm.cursorCoords(Pos(0, 0)), cur1 = cm.cursorCoords(Pos(0, 10));
   1.967 +  is(cur0.top < cur1.top);
   1.968 +  is(cur0.bottom < cur1.bottom);
   1.969 +  var curL = cm.cursorCoords(Pos(0, 6)), curR = cm.cursorCoords(Pos(0, 9));
   1.970 +  eq(curL.top, cur0.top);
   1.971 +  eq(curL.bottom, cur0.bottom);
   1.972 +  eq(curR.top, cur1.top);
   1.973 +  eq(curR.bottom, cur1.bottom);
   1.974 +  cm.replaceRange("", Pos(0, 9), Pos(0));
   1.975 +  curR = cm.cursorCoords(Pos(0, 9));
   1.976 +  if (phantom) return;
   1.977 +  eq(curR.top, cur1.top);
   1.978 +  eq(curR.bottom, cur1.bottom);
   1.979 +}, {value: "1 2 3 xxx 4", lineWrapping: true});
   1.980 +
   1.981 +testCM("changedInlineWidget", function(cm) {
   1.982 +  cm.setSize("10em");
   1.983 +  var w = document.createElement("span");
   1.984 +  w.innerHTML = "x";
   1.985 +  var m = cm.markText(Pos(0, 4), Pos(0, 5), {replacedWith: w});
   1.986 +  w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed";
   1.987 +  m.changed();
   1.988 +  var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0];
   1.989 +  is(hScroll.scrollWidth > hScroll.clientWidth);
   1.990 +}, {value: "hello there"});
   1.991 +
   1.992 +testCM("changedBookmark", function(cm) {
   1.993 +  cm.setSize("10em");
   1.994 +  var w = document.createElement("span");
   1.995 +  w.innerHTML = "x";
   1.996 +  var m = cm.setBookmark(Pos(0, 4), {widget: w});
   1.997 +  w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed";
   1.998 +  m.changed();
   1.999 +  var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0];
  1.1000 +  is(hScroll.scrollWidth > hScroll.clientWidth);
  1.1001 +}, {value: "abcdefg"});
  1.1002 +
  1.1003 +testCM("inlineWidget", function(cm) {
  1.1004 +  var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")});
  1.1005 +  cm.setCursor(0, 2);
  1.1006 +  CodeMirror.commands.goLineDown(cm);
  1.1007 +  eqPos(cm.getCursor(), Pos(1, 4));
  1.1008 +  cm.setCursor(0, 2);
  1.1009 +  cm.replaceSelection("hi");
  1.1010 +  eqPos(w.find(), Pos(0, 2));
  1.1011 +  cm.setCursor(0, 1);
  1.1012 +  cm.replaceSelection("ay");
  1.1013 +  eqPos(w.find(), Pos(0, 4));
  1.1014 +  eq(cm.getLine(0), "uayuhiuu");
  1.1015 +}, {value: "uuuu\nuuuuuu"});
  1.1016 +
  1.1017 +testCM("wrappingAndResizing", function(cm) {
  1.1018 +  cm.setSize(null, "auto");
  1.1019 +  cm.setOption("lineWrapping", true);
  1.1020 +  var wrap = cm.getWrapperElement(), h0 = wrap.offsetHeight;
  1.1021 +  var doc = "xxx xxx xxx xxx xxx";
  1.1022 +  cm.setValue(doc);
  1.1023 +  for (var step = 10, w = cm.charCoords(Pos(0, 18), "div").right;; w += step) {
  1.1024 +    cm.setSize(w);
  1.1025 +    if (wrap.offsetHeight <= h0 * (opera_lt10 ? 1.2 : 1.5)) {
  1.1026 +      if (step == 10) { w -= 10; step = 1; }
  1.1027 +      else break;
  1.1028 +    }
  1.1029 +  }
  1.1030 +  // Ensure that putting the cursor at the end of the maximally long
  1.1031 +  // line doesn't cause wrapping to happen.
  1.1032 +  cm.setCursor(Pos(0, doc.length));
  1.1033 +  eq(wrap.offsetHeight, h0);
  1.1034 +  cm.replaceSelection("x");
  1.1035 +  is(wrap.offsetHeight > h0, "wrapping happens");
  1.1036 +  // Now add a max-height and, in a document consisting of
  1.1037 +  // almost-wrapped lines, go over it so that a scrollbar appears.
  1.1038 +  cm.setValue(doc + "\n" + doc + "\n");
  1.1039 +  cm.getScrollerElement().style.maxHeight = "100px";
  1.1040 +  cm.replaceRange("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n!\n", Pos(2, 0));
  1.1041 +  forEach([Pos(0, doc.length), Pos(0, doc.length - 1),
  1.1042 +           Pos(0, 0), Pos(1, doc.length), Pos(1, doc.length - 1)],
  1.1043 +          function(pos) {
  1.1044 +    var coords = cm.charCoords(pos);
  1.1045 +    eqPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5}));
  1.1046 +  });
  1.1047 +}, null, ie_lt8);
  1.1048 +
  1.1049 +testCM("measureEndOfLine", function(cm) {
  1.1050 +  cm.setSize(null, "auto");
  1.1051 +  var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild;
  1.1052 +  var lh = inner.offsetHeight;
  1.1053 +  for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) {
  1.1054 +    cm.setSize(w);
  1.1055 +    if (inner.offsetHeight < 2.5 * lh) {
  1.1056 +      if (step == 10) { w -= 10; step = 1; }
  1.1057 +      else break;
  1.1058 +    }
  1.1059 +  }
  1.1060 +  cm.setValue(cm.getValue() + "\n\n");
  1.1061 +  var endPos = cm.charCoords(Pos(0, 18), "local");
  1.1062 +  is(endPos.top > lh * .8, "not at top");
  1.1063 +  is(endPos.left > w - 20, "not at right");
  1.1064 +  endPos = cm.charCoords(Pos(0, 18));
  1.1065 +  eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18));
  1.1066 +}, {mode: "text/html", value: "<!-- foo barrr -->", lineWrapping: true}, ie_lt8 || opera_lt10);
  1.1067 +
  1.1068 +testCM("scrollVerticallyAndHorizontally", function(cm) {
  1.1069 +  cm.setSize(100, 100);
  1.1070 +  addDoc(cm, 40, 40);
  1.1071 +  cm.setCursor(39);
  1.1072 +  var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-vscrollbar")[0];
  1.1073 +  is(bar.offsetHeight < wrap.offsetHeight, "vertical scrollbar limited by horizontal one");
  1.1074 +  var cursorBox = byClassName(wrap, "CodeMirror-cursor")[0].getBoundingClientRect();
  1.1075 +  var editorBox = wrap.getBoundingClientRect();
  1.1076 +  is(cursorBox.bottom < editorBox.top + cm.getScrollerElement().clientHeight,
  1.1077 +     "bottom line visible");
  1.1078 +}, {lineNumbers: true});
  1.1079 +
  1.1080 +testCM("moveVstuck", function(cm) {
  1.1081 +  var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight;
  1.1082 +  var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n";
  1.1083 +  cm.setValue(val);
  1.1084 +  for (var w = cm.charCoords(Pos(0, 26), "div").right * 2.8;; w += 5) {
  1.1085 +    cm.setSize(w);
  1.1086 +    if (lines.offsetHeight <= 3.5 * h0) break;
  1.1087 +  }
  1.1088 +  cm.setCursor(Pos(0, val.length - 1));
  1.1089 +  cm.moveV(-1, "line");
  1.1090 +  eqPos(cm.getCursor(), Pos(0, 26));
  1.1091 +}, {lineWrapping: true}, ie_lt8 || opera_lt10);
  1.1092 +
  1.1093 +testCM("collapseOnMove", function(cm) {
  1.1094 +  cm.setSelection(Pos(0, 1), Pos(2, 4));
  1.1095 +  cm.execCommand("goLineUp");
  1.1096 +  is(!cm.somethingSelected());
  1.1097 +  eqPos(cm.getCursor(), Pos(0, 1));
  1.1098 +  cm.setSelection(Pos(0, 1), Pos(2, 4));
  1.1099 +  cm.execCommand("goPageDown");
  1.1100 +  is(!cm.somethingSelected());
  1.1101 +  eqPos(cm.getCursor(), Pos(2, 4));
  1.1102 +  cm.execCommand("goLineUp");
  1.1103 +  cm.execCommand("goLineUp");
  1.1104 +  eqPos(cm.getCursor(), Pos(0, 4));
  1.1105 +  cm.setSelection(Pos(0, 1), Pos(2, 4));
  1.1106 +  cm.execCommand("goCharLeft");
  1.1107 +  is(!cm.somethingSelected());
  1.1108 +  eqPos(cm.getCursor(), Pos(0, 1));
  1.1109 +}, {value: "aaaaa\nb\nccccc"});
  1.1110 +
  1.1111 +testCM("clickTab", function(cm) {
  1.1112 +  var p0 = cm.charCoords(Pos(0, 0));
  1.1113 +  eqPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0));
  1.1114 +  eqPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), Pos(0, 1));
  1.1115 +}, {value: "\t\n\n", lineWrapping: true, tabSize: 8});
  1.1116 +
  1.1117 +testCM("verticalScroll", function(cm) {
  1.1118 +  cm.setSize(100, 200);
  1.1119 +  cm.setValue("foo\nbar\nbaz\n");
  1.1120 +  var sc = cm.getScrollerElement(), baseWidth = sc.scrollWidth;
  1.1121 +  cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0));
  1.1122 +  is(sc.scrollWidth > baseWidth, "scrollbar present");
  1.1123 +  cm.replaceRange("foo", Pos(0, 0), Pos(0));
  1.1124 +  if (!phantom) eq(sc.scrollWidth, baseWidth, "scrollbar gone");
  1.1125 +  cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0));
  1.1126 +  cm.replaceRange("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh", Pos(1, 0), Pos(1));
  1.1127 +  is(sc.scrollWidth > baseWidth, "present again");
  1.1128 +  var curWidth = sc.scrollWidth;
  1.1129 +  cm.replaceRange("foo", Pos(0, 0), Pos(0));
  1.1130 +  is(sc.scrollWidth < curWidth, "scrollbar smaller");
  1.1131 +  is(sc.scrollWidth > baseWidth, "but still present");
  1.1132 +});
  1.1133 +
  1.1134 +testCM("extraKeys", function(cm) {
  1.1135 +  var outcome;
  1.1136 +  function fakeKey(expected, code, props) {
  1.1137 +    if (typeof code == "string") code = code.charCodeAt(0);
  1.1138 +    var e = {type: "keydown", keyCode: code, preventDefault: function(){}, stopPropagation: function(){}};
  1.1139 +    if (props) for (var n in props) e[n] = props[n];
  1.1140 +    outcome = null;
  1.1141 +    cm.triggerOnKeyDown(e);
  1.1142 +    eq(outcome, expected);
  1.1143 +  }
  1.1144 +  CodeMirror.commands.testCommand = function() {outcome = "tc";};
  1.1145 +  CodeMirror.commands.goTestCommand = function() {outcome = "gtc";};
  1.1146 +  cm.setOption("extraKeys", {"Shift-X": function() {outcome = "sx";},
  1.1147 +                             "X": function() {outcome = "x";},
  1.1148 +                             "Ctrl-Alt-U": function() {outcome = "cau";},
  1.1149 +                             "End": "testCommand",
  1.1150 +                             "Home": "goTestCommand",
  1.1151 +                             "Tab": false});
  1.1152 +  fakeKey(null, "U");
  1.1153 +  fakeKey("cau", "U", {ctrlKey: true, altKey: true});
  1.1154 +  fakeKey(null, "U", {shiftKey: true, ctrlKey: true, altKey: true});
  1.1155 +  fakeKey("x", "X");
  1.1156 +  fakeKey("sx", "X", {shiftKey: true});
  1.1157 +  fakeKey("tc", 35);
  1.1158 +  fakeKey(null, 35, {shiftKey: true});
  1.1159 +  fakeKey("gtc", 36);
  1.1160 +  fakeKey("gtc", 36, {shiftKey: true});
  1.1161 +  fakeKey(null, 9);
  1.1162 +}, null, window.opera && mac);
  1.1163 +
  1.1164 +testCM("wordMovementCommands", function(cm) {
  1.1165 +  cm.execCommand("goWordLeft");
  1.1166 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1167 +  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
  1.1168 +  eqPos(cm.getCursor(), Pos(0, 7));
  1.1169 +  cm.execCommand("goWordLeft");
  1.1170 +  eqPos(cm.getCursor(), Pos(0, 5));
  1.1171 +  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
  1.1172 +  eqPos(cm.getCursor(), Pos(0, 12));
  1.1173 +  cm.execCommand("goWordLeft");
  1.1174 +  eqPos(cm.getCursor(), Pos(0, 9));
  1.1175 +  cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
  1.1176 +  eqPos(cm.getCursor(), Pos(0, 24));
  1.1177 +  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
  1.1178 +  eqPos(cm.getCursor(), Pos(1, 9));
  1.1179 +  cm.execCommand("goWordRight");
  1.1180 +  eqPos(cm.getCursor(), Pos(1, 13));
  1.1181 +  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
  1.1182 +  eqPos(cm.getCursor(), Pos(2, 0));
  1.1183 +}, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"});
  1.1184 +
  1.1185 +testCM("groupMovementCommands", function(cm) {
  1.1186 +  cm.execCommand("goGroupLeft");
  1.1187 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1188 +  cm.execCommand("goGroupRight");
  1.1189 +  eqPos(cm.getCursor(), Pos(0, 4));
  1.1190 +  cm.execCommand("goGroupRight");
  1.1191 +  eqPos(cm.getCursor(), Pos(0, 7));
  1.1192 +  cm.execCommand("goGroupRight");
  1.1193 +  eqPos(cm.getCursor(), Pos(0, 10));
  1.1194 +  cm.execCommand("goGroupLeft");
  1.1195 +  eqPos(cm.getCursor(), Pos(0, 7));
  1.1196 +  cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight");
  1.1197 +  eqPos(cm.getCursor(), Pos(0, 15));
  1.1198 +  cm.setCursor(Pos(0, 17));
  1.1199 +  cm.execCommand("goGroupLeft");
  1.1200 +  eqPos(cm.getCursor(), Pos(0, 16));
  1.1201 +  cm.execCommand("goGroupLeft");
  1.1202 +  eqPos(cm.getCursor(), Pos(0, 14));
  1.1203 +  cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight");
  1.1204 +  eqPos(cm.getCursor(), Pos(0, 20));
  1.1205 +  cm.execCommand("goGroupRight");
  1.1206 +  eqPos(cm.getCursor(), Pos(1, 0));
  1.1207 +  cm.execCommand("goGroupRight");
  1.1208 +  eqPos(cm.getCursor(), Pos(1, 2));
  1.1209 +  cm.execCommand("goGroupRight");
  1.1210 +  eqPos(cm.getCursor(), Pos(1, 5));
  1.1211 +  cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft");
  1.1212 +  eqPos(cm.getCursor(), Pos(1, 0));
  1.1213 +  cm.execCommand("goGroupLeft");
  1.1214 +  eqPos(cm.getCursor(), Pos(0, 20));
  1.1215 +  cm.execCommand("goGroupLeft");
  1.1216 +  eqPos(cm.getCursor(), Pos(0, 16));
  1.1217 +}, {value: "booo ba---quux. ffff\n  abc d"});
  1.1218 +
  1.1219 +testCM("groupsAndWhitespace", function(cm) {
  1.1220 +  var positions = [Pos(0, 0), Pos(0, 2), Pos(0, 5), Pos(0, 9), Pos(0, 11),
  1.1221 +                   Pos(1, 0), Pos(1, 2), Pos(1, 5)];
  1.1222 +  for (var i = 1; i < positions.length; i++) {
  1.1223 +    cm.execCommand("goGroupRight");
  1.1224 +    eqPos(cm.getCursor(), positions[i]);
  1.1225 +  }
  1.1226 +  for (var i = positions.length - 2; i >= 0; i--) {
  1.1227 +    cm.execCommand("goGroupLeft");
  1.1228 +    eqPos(cm.getCursor(), i == 2 ? Pos(0, 6) : positions[i]);
  1.1229 +  }
  1.1230 +}, {value: "  foo +++  \n  bar"});
  1.1231 +
  1.1232 +testCM("charMovementCommands", function(cm) {
  1.1233 +  cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft");
  1.1234 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1235 +  cm.execCommand("goCharRight"); cm.execCommand("goCharRight");
  1.1236 +  eqPos(cm.getCursor(), Pos(0, 2));
  1.1237 +  cm.setCursor(Pos(1, 0));
  1.1238 +  cm.execCommand("goColumnLeft");
  1.1239 +  eqPos(cm.getCursor(), Pos(1, 0));
  1.1240 +  cm.execCommand("goCharLeft");
  1.1241 +  eqPos(cm.getCursor(), Pos(0, 5));
  1.1242 +  cm.execCommand("goColumnRight");
  1.1243 +  eqPos(cm.getCursor(), Pos(0, 5));
  1.1244 +  cm.execCommand("goCharRight");
  1.1245 +  eqPos(cm.getCursor(), Pos(1, 0));
  1.1246 +  cm.execCommand("goLineEnd");
  1.1247 +  eqPos(cm.getCursor(), Pos(1, 5));
  1.1248 +  cm.execCommand("goLineStartSmart");
  1.1249 +  eqPos(cm.getCursor(), Pos(1, 1));
  1.1250 +  cm.execCommand("goLineStartSmart");
  1.1251 +  eqPos(cm.getCursor(), Pos(1, 0));
  1.1252 +  cm.setCursor(Pos(2, 0));
  1.1253 +  cm.execCommand("goCharRight"); cm.execCommand("goColumnRight");
  1.1254 +  eqPos(cm.getCursor(), Pos(2, 0));
  1.1255 +}, {value: "line1\n ine2\n"});
  1.1256 +
  1.1257 +testCM("verticalMovementCommands", function(cm) {
  1.1258 +  cm.execCommand("goLineUp");
  1.1259 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1260 +  cm.execCommand("goLineDown");
  1.1261 +  if (!phantom) // This fails in PhantomJS, though not in a real Webkit
  1.1262 +    eqPos(cm.getCursor(), Pos(1, 0));
  1.1263 +  cm.setCursor(Pos(1, 12));
  1.1264 +  cm.execCommand("goLineDown");
  1.1265 +  eqPos(cm.getCursor(), Pos(2, 5));
  1.1266 +  cm.execCommand("goLineDown");
  1.1267 +  eqPos(cm.getCursor(), Pos(3, 0));
  1.1268 +  cm.execCommand("goLineUp");
  1.1269 +  eqPos(cm.getCursor(), Pos(2, 5));
  1.1270 +  cm.execCommand("goLineUp");
  1.1271 +  eqPos(cm.getCursor(), Pos(1, 12));
  1.1272 +  cm.execCommand("goPageDown");
  1.1273 +  eqPos(cm.getCursor(), Pos(5, 0));
  1.1274 +  cm.execCommand("goPageDown"); cm.execCommand("goLineDown");
  1.1275 +  eqPos(cm.getCursor(), Pos(5, 0));
  1.1276 +  cm.execCommand("goPageUp");
  1.1277 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1278 +}, {value: "line1\nlong long line2\nline3\n\nline5\n"});
  1.1279 +
  1.1280 +testCM("verticalMovementCommandsWrapping", function(cm) {
  1.1281 +  cm.setSize(120);
  1.1282 +  cm.setCursor(Pos(0, 5));
  1.1283 +  cm.execCommand("goLineDown");
  1.1284 +  eq(cm.getCursor().line, 0);
  1.1285 +  is(cm.getCursor().ch > 5, "moved beyond wrap");
  1.1286 +  for (var i = 0; ; ++i) {
  1.1287 +    is(i < 20, "no endless loop");
  1.1288 +    cm.execCommand("goLineDown");
  1.1289 +    var cur = cm.getCursor();
  1.1290 +    if (cur.line == 1) eq(cur.ch, 5);
  1.1291 +    if (cur.line == 2) { eq(cur.ch, 1); break; }
  1.1292 +  }
  1.1293 +}, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk",
  1.1294 +    lineWrapping: true});
  1.1295 +
  1.1296 +testCM("rtlMovement", function(cm) {
  1.1297 +  forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج",
  1.1298 +           "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق",
  1.1299 +           "<img src=\"/בדיקה3.jpg\">"], function(line) {
  1.1300 +    var inv = line.charAt(0) == "خ";
  1.1301 +    cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart");
  1.1302 +    var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0];
  1.1303 +    var cursor = cursors.firstChild;
  1.1304 +    var prevX = cursor.offsetLeft, prevY = cursor.offsetTop;
  1.1305 +    for (var i = 0; i <= line.length; ++i) {
  1.1306 +      cm.execCommand("goCharRight");
  1.1307 +      cursor = cursors.firstChild;
  1.1308 +      if (i == line.length) is(cursor.offsetTop > prevY, "next line");
  1.1309 +      else is(cursor.offsetLeft > prevX, "moved right");
  1.1310 +      prevX = cursor.offsetLeft; prevY = cursor.offsetTop;
  1.1311 +    }
  1.1312 +    cm.setCursor(0, 0); cm.execCommand(inv ? "goLineStart" : "goLineEnd");
  1.1313 +    prevX = cursors.firstChild.offsetLeft;
  1.1314 +    for (var i = 0; i < line.length; ++i) {
  1.1315 +      cm.execCommand("goCharLeft");
  1.1316 +      cursor = cursors.firstChild;
  1.1317 +      is(cursor.offsetLeft < prevX, "moved left");
  1.1318 +      prevX = cursor.offsetLeft;
  1.1319 +    }
  1.1320 +  });
  1.1321 +}, null, ie_lt9);
  1.1322 +
  1.1323 +// Verify that updating a line clears its bidi ordering
  1.1324 +testCM("bidiUpdate", function(cm) {
  1.1325 +  cm.setCursor(Pos(0, 2));
  1.1326 +  cm.replaceSelection("خحج", "start");
  1.1327 +  cm.execCommand("goCharRight");
  1.1328 +  eqPos(cm.getCursor(), Pos(0, 4));
  1.1329 +}, {value: "abcd\n"});
  1.1330 +
  1.1331 +testCM("movebyTextUnit", function(cm) {
  1.1332 +  cm.setValue("בְּרֵאשִ\nééé́\n");
  1.1333 +  cm.execCommand("goLineEnd");
  1.1334 +  for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight");
  1.1335 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1336 +  cm.execCommand("goCharRight");
  1.1337 +  eqPos(cm.getCursor(), Pos(1, 0));
  1.1338 +  cm.execCommand("goCharRight");
  1.1339 +  cm.execCommand("goCharRight");
  1.1340 +  eqPos(cm.getCursor(), Pos(1, 4));
  1.1341 +  cm.execCommand("goCharRight");
  1.1342 +  eqPos(cm.getCursor(), Pos(1, 7));
  1.1343 +});
  1.1344 +
  1.1345 +testCM("lineChangeEvents", function(cm) {
  1.1346 +  addDoc(cm, 3, 5);
  1.1347 +  var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"];
  1.1348 +  for (var i = 0; i < 5; ++i) {
  1.1349 +    CodeMirror.on(cm.getLineHandle(i), "delete", function(i) {
  1.1350 +      return function() {log.push("del " + i);};
  1.1351 +    }(i));
  1.1352 +    CodeMirror.on(cm.getLineHandle(i), "change", function(i) {
  1.1353 +      return function() {log.push("ch " + i);};
  1.1354 +    }(i));
  1.1355 +  }
  1.1356 +  cm.replaceRange("x", Pos(0, 1));
  1.1357 +  cm.replaceRange("xy", Pos(1, 1), Pos(2));
  1.1358 +  cm.replaceRange("foo\nbar", Pos(0, 1));
  1.1359 +  cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount()));
  1.1360 +  eq(log.length, want.length, "same length");
  1.1361 +  for (var i = 0; i < log.length; ++i)
  1.1362 +    eq(log[i], want[i]);
  1.1363 +});
  1.1364 +
  1.1365 +testCM("scrollEntirelyToRight", function(cm) {
  1.1366 +  if (phantom) return;
  1.1367 +  addDoc(cm, 500, 2);
  1.1368 +  cm.setCursor(Pos(0, 500));
  1.1369 +  var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0];
  1.1370 +  is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left);
  1.1371 +});
  1.1372 +
  1.1373 +testCM("lineWidgets", function(cm) {
  1.1374 +  addDoc(cm, 500, 3);
  1.1375 +  var last = cm.charCoords(Pos(2, 0));
  1.1376 +  var node = document.createElement("div");
  1.1377 +  node.innerHTML = "hi";
  1.1378 +  var widget = cm.addLineWidget(1, node);
  1.1379 +  is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space");
  1.1380 +  cm.setCursor(Pos(1, 1));
  1.1381 +  cm.execCommand("goLineDown");
  1.1382 +  eqPos(cm.getCursor(), Pos(2, 1));
  1.1383 +  cm.execCommand("goLineUp");
  1.1384 +  eqPos(cm.getCursor(), Pos(1, 1));
  1.1385 +});
  1.1386 +
  1.1387 +testCM("lineWidgetFocus", function(cm) {
  1.1388 +  var place = document.getElementById("testground");
  1.1389 +  place.className = "offscreen";
  1.1390 +  try {
  1.1391 +    addDoc(cm, 500, 10);
  1.1392 +    var node = document.createElement("input");
  1.1393 +    var widget = cm.addLineWidget(1, node);
  1.1394 +    node.focus();
  1.1395 +    eq(document.activeElement, node);
  1.1396 +    cm.replaceRange("new stuff", Pos(1, 0));
  1.1397 +    eq(document.activeElement, node);
  1.1398 +  } finally {
  1.1399 +    place.className = "";
  1.1400 +  }
  1.1401 +});
  1.1402 +
  1.1403 +testCM("lineWidgetCautiousRedraw", function(cm) {
  1.1404 +  var node = document.createElement("div");
  1.1405 +  node.innerHTML = "hahah";
  1.1406 +  var w = cm.addLineWidget(0, node);
  1.1407 +  var redrawn = false;
  1.1408 +  w.on("redraw", function() { redrawn = true; });
  1.1409 +  cm.replaceSelection("0");
  1.1410 +  is(!redrawn);
  1.1411 +}, {value: "123\n456"});
  1.1412 +
  1.1413 +testCM("lineWidgetChanged", function(cm) {
  1.1414 +  addDoc(cm, 2, 300);
  1.1415 +  cm.setSize(null, cm.defaultTextHeight() * 50);
  1.1416 +  cm.scrollTo(null, cm.heightAtLine(125, "local"));
  1.1417 +  function w() {
  1.1418 +    var node = document.createElement("div");
  1.1419 +    node.style.cssText = "background: yellow; height: 50px;";
  1.1420 +    return node;
  1.1421 +  }
  1.1422 +  var info0 = cm.getScrollInfo();
  1.1423 +  var w0 = cm.addLineWidget(0, w());
  1.1424 +  var w150 = cm.addLineWidget(150, w());
  1.1425 +  var w300 = cm.addLineWidget(300, w());
  1.1426 +  var info1 = cm.getScrollInfo();
  1.1427 +  eq(info0.height + 150, info1.height);
  1.1428 +  eq(info0.top + 50, info1.top);
  1.1429 +  w0.node.style.height = w150.node.style.height = w300.node.style.height = "10px";
  1.1430 +  w0.changed(); w150.changed(); w300.changed();
  1.1431 +  var info2 = cm.getScrollInfo();
  1.1432 +  eq(info0.height + 30, info2.height);
  1.1433 +  eq(info0.top + 10, info2.top);
  1.1434 +});
  1.1435 +
  1.1436 +testCM("getLineNumber", function(cm) {
  1.1437 +  addDoc(cm, 2, 20);
  1.1438 +  var h1 = cm.getLineHandle(1);
  1.1439 +  eq(cm.getLineNumber(h1), 1);
  1.1440 +  cm.replaceRange("hi\nbye\n", Pos(0, 0));
  1.1441 +  eq(cm.getLineNumber(h1), 3);
  1.1442 +  cm.setValue("");
  1.1443 +  eq(cm.getLineNumber(h1), null);
  1.1444 +});
  1.1445 +
  1.1446 +testCM("jumpTheGap", function(cm) {
  1.1447 +  if (phantom) return;
  1.1448 +  var longLine = "abcdef ghiklmnop qrstuvw xyz ";
  1.1449 +  longLine += longLine; longLine += longLine; longLine += longLine;
  1.1450 +  cm.replaceRange(longLine, Pos(2, 0), Pos(2));
  1.1451 +  cm.setSize("200px", null);
  1.1452 +  cm.getWrapperElement().style.lineHeight = 2;
  1.1453 +  cm.refresh();
  1.1454 +  cm.setCursor(Pos(0, 1));
  1.1455 +  cm.execCommand("goLineDown");
  1.1456 +  eqPos(cm.getCursor(), Pos(1, 1));
  1.1457 +  cm.execCommand("goLineDown");
  1.1458 +  eqPos(cm.getCursor(), Pos(2, 1));
  1.1459 +  cm.execCommand("goLineDown");
  1.1460 +  eq(cm.getCursor().line, 2);
  1.1461 +  is(cm.getCursor().ch > 1);
  1.1462 +  cm.execCommand("goLineUp");
  1.1463 +  eqPos(cm.getCursor(), Pos(2, 1));
  1.1464 +  cm.execCommand("goLineUp");
  1.1465 +  eqPos(cm.getCursor(), Pos(1, 1));
  1.1466 +  var node = document.createElement("div");
  1.1467 +  node.innerHTML = "hi"; node.style.height = "30px";
  1.1468 +  cm.addLineWidget(0, node);
  1.1469 +  cm.addLineWidget(1, node.cloneNode(true), {above: true});
  1.1470 +  cm.setCursor(Pos(0, 2));
  1.1471 +  cm.execCommand("goLineDown");
  1.1472 +  eqPos(cm.getCursor(), Pos(1, 2));
  1.1473 +  cm.execCommand("goLineUp");
  1.1474 +  eqPos(cm.getCursor(), Pos(0, 2));
  1.1475 +}, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"});
  1.1476 +
  1.1477 +testCM("addLineClass", function(cm) {
  1.1478 +  function cls(line, text, bg, wrap) {
  1.1479 +    var i = cm.lineInfo(line);
  1.1480 +    eq(i.textClass, text);
  1.1481 +    eq(i.bgClass, bg);
  1.1482 +    eq(i.wrapClass, wrap);
  1.1483 +  }
  1.1484 +  cm.addLineClass(0, "text", "foo");
  1.1485 +  cm.addLineClass(0, "text", "bar");
  1.1486 +  cm.addLineClass(1, "background", "baz");
  1.1487 +  cm.addLineClass(1, "wrap", "foo");
  1.1488 +  cls(0, "foo bar", null, null);
  1.1489 +  cls(1, null, "baz", "foo");
  1.1490 +  var lines = cm.display.lineDiv;
  1.1491 +  eq(byClassName(lines, "foo").length, 2);
  1.1492 +  eq(byClassName(lines, "bar").length, 1);
  1.1493 +  eq(byClassName(lines, "baz").length, 1);
  1.1494 +  cm.removeLineClass(0, "text", "foo");
  1.1495 +  cls(0, "bar", null, null);
  1.1496 +  cm.removeLineClass(0, "text", "foo");
  1.1497 +  cls(0, "bar", null, null);
  1.1498 +  cm.removeLineClass(0, "text", "bar");
  1.1499 +  cls(0, null, null, null);
  1.1500 +  cm.addLineClass(1, "wrap", "quux");
  1.1501 +  cls(1, null, "baz", "foo quux");
  1.1502 +  cm.removeLineClass(1, "wrap");
  1.1503 +  cls(1, null, "baz", null);
  1.1504 +}, {value: "hohoho\n"});
  1.1505 +
  1.1506 +testCM("atomicMarker", function(cm) {
  1.1507 +  addDoc(cm, 10, 10);
  1.1508 +  function atom(ll, cl, lr, cr, li, ri) {
  1.1509 +    return cm.markText(Pos(ll, cl), Pos(lr, cr),
  1.1510 +                       {atomic: true, inclusiveLeft: li, inclusiveRight: ri});
  1.1511 +  }
  1.1512 +  var m = atom(0, 1, 0, 5);
  1.1513 +  cm.setCursor(Pos(0, 1));
  1.1514 +  cm.execCommand("goCharRight");
  1.1515 +  eqPos(cm.getCursor(), Pos(0, 5));
  1.1516 +  cm.execCommand("goCharLeft");
  1.1517 +  eqPos(cm.getCursor(), Pos(0, 1));
  1.1518 +  m.clear();
  1.1519 +  m = atom(0, 0, 0, 5, true);
  1.1520 +  eqPos(cm.getCursor(), Pos(0, 5), "pushed out");
  1.1521 +  cm.execCommand("goCharLeft");
  1.1522 +  eqPos(cm.getCursor(), Pos(0, 5));
  1.1523 +  m.clear();
  1.1524 +  m = atom(8, 4, 9, 10, false, true);
  1.1525 +  cm.setCursor(Pos(9, 8));
  1.1526 +  eqPos(cm.getCursor(), Pos(8, 4), "set");
  1.1527 +  cm.execCommand("goCharRight");
  1.1528 +  eqPos(cm.getCursor(), Pos(8, 4), "char right");
  1.1529 +  cm.execCommand("goLineDown");
  1.1530 +  eqPos(cm.getCursor(), Pos(8, 4), "line down");
  1.1531 +  cm.execCommand("goCharLeft");
  1.1532 +  eqPos(cm.getCursor(), Pos(8, 3));
  1.1533 +  m.clear();
  1.1534 +  m = atom(1, 1, 3, 8);
  1.1535 +  cm.setCursor(Pos(0, 0));
  1.1536 +  cm.setCursor(Pos(2, 0));
  1.1537 +  eqPos(cm.getCursor(), Pos(3, 8));
  1.1538 +  cm.execCommand("goCharLeft");
  1.1539 +  eqPos(cm.getCursor(), Pos(1, 1));
  1.1540 +  cm.execCommand("goCharRight");
  1.1541 +  eqPos(cm.getCursor(), Pos(3, 8));
  1.1542 +  cm.execCommand("goLineUp");
  1.1543 +  eqPos(cm.getCursor(), Pos(1, 1));
  1.1544 +  cm.execCommand("goLineDown");
  1.1545 +  eqPos(cm.getCursor(), Pos(3, 8));
  1.1546 +  cm.execCommand("delCharBefore");
  1.1547 +  eq(cm.getValue().length, 80, "del chunk");
  1.1548 +  m = atom(3, 0, 5, 5);
  1.1549 +  cm.setCursor(Pos(3, 0));
  1.1550 +  cm.execCommand("delWordAfter");
  1.1551 +  eq(cm.getValue().length, 53, "del chunk");
  1.1552 +});
  1.1553 +
  1.1554 +testCM("readOnlyMarker", function(cm) {
  1.1555 +  function mark(ll, cl, lr, cr, at) {
  1.1556 +    return cm.markText(Pos(ll, cl), Pos(lr, cr),
  1.1557 +                       {readOnly: true, atomic: at});
  1.1558 +  }
  1.1559 +  var m = mark(0, 1, 0, 4);
  1.1560 +  cm.setCursor(Pos(0, 2));
  1.1561 +  cm.replaceSelection("hi", "end");
  1.1562 +  eqPos(cm.getCursor(), Pos(0, 2));
  1.1563 +  eq(cm.getLine(0), "abcde");
  1.1564 +  cm.execCommand("selectAll");
  1.1565 +  cm.replaceSelection("oops", "around");
  1.1566 +  eq(cm.getValue(), "oopsbcd");
  1.1567 +  cm.undo();
  1.1568 +  eqPos(m.find().from, Pos(0, 1));
  1.1569 +  eqPos(m.find().to, Pos(0, 4));
  1.1570 +  m.clear();
  1.1571 +  cm.setCursor(Pos(0, 2));
  1.1572 +  cm.replaceSelection("hi", "around");
  1.1573 +  eq(cm.getLine(0), "abhicde");
  1.1574 +  eqPos(cm.getCursor(), Pos(0, 4));
  1.1575 +  m = mark(0, 2, 2, 2, true);
  1.1576 +  cm.setSelection(Pos(1, 1), Pos(2, 4));
  1.1577 +  cm.replaceSelection("t", "end");
  1.1578 +  eqPos(cm.getCursor(), Pos(2, 3));
  1.1579 +  eq(cm.getLine(2), "klto");
  1.1580 +  cm.execCommand("goCharLeft");
  1.1581 +  cm.execCommand("goCharLeft");
  1.1582 +  eqPos(cm.getCursor(), Pos(0, 2));
  1.1583 +  cm.setSelection(Pos(0, 1), Pos(0, 3));
  1.1584 +  cm.replaceSelection("xx", "around");
  1.1585 +  eqPos(cm.getCursor(), Pos(0, 3));
  1.1586 +  eq(cm.getLine(0), "axxhicde");
  1.1587 +}, {value: "abcde\nfghij\nklmno\n"});
  1.1588 +
  1.1589 +testCM("dirtyBit", function(cm) {
  1.1590 +  eq(cm.isClean(), true);
  1.1591 +  cm.replaceSelection("boo", null, "test");
  1.1592 +  eq(cm.isClean(), false);
  1.1593 +  cm.undo();
  1.1594 +  eq(cm.isClean(), true);
  1.1595 +  cm.replaceSelection("boo", null, "test");
  1.1596 +  cm.replaceSelection("baz", null, "test");
  1.1597 +  cm.undo();
  1.1598 +  eq(cm.isClean(), false);
  1.1599 +  cm.markClean();
  1.1600 +  eq(cm.isClean(), true);
  1.1601 +  cm.undo();
  1.1602 +  eq(cm.isClean(), false);
  1.1603 +  cm.redo();
  1.1604 +  eq(cm.isClean(), true);
  1.1605 +});
  1.1606 +
  1.1607 +testCM("changeGeneration", function(cm) {
  1.1608 +  cm.replaceSelection("x");
  1.1609 +  var softGen = cm.changeGeneration();
  1.1610 +  cm.replaceSelection("x");
  1.1611 +  cm.undo();
  1.1612 +  eq(cm.getValue(), "");
  1.1613 +  is(!cm.isClean(softGen));
  1.1614 +  cm.replaceSelection("x");
  1.1615 +  var hardGen = cm.changeGeneration(true);
  1.1616 +  cm.replaceSelection("x");
  1.1617 +  cm.undo();
  1.1618 +  eq(cm.getValue(), "x");
  1.1619 +  is(cm.isClean(hardGen));
  1.1620 +});
  1.1621 +
  1.1622 +testCM("addKeyMap", function(cm) {
  1.1623 +  function sendKey(code) {
  1.1624 +    cm.triggerOnKeyDown({type: "keydown", keyCode: code,
  1.1625 +                         preventDefault: function(){}, stopPropagation: function(){}});
  1.1626 +  }
  1.1627 +
  1.1628 +  sendKey(39);
  1.1629 +  eqPos(cm.getCursor(), Pos(0, 1));
  1.1630 +  var test = 0;
  1.1631 +  var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }}
  1.1632 +  cm.addKeyMap(map1);
  1.1633 +  sendKey(39);
  1.1634 +  eqPos(cm.getCursor(), Pos(0, 1));
  1.1635 +  eq(test, 1);
  1.1636 +  cm.addKeyMap(map2, true);
  1.1637 +  sendKey(39);
  1.1638 +  eq(test, 2);
  1.1639 +  cm.removeKeyMap(map1);
  1.1640 +  sendKey(39);
  1.1641 +  eq(test, 12);
  1.1642 +  cm.removeKeyMap(map2);
  1.1643 +  sendKey(39);
  1.1644 +  eq(test, 12);
  1.1645 +  eqPos(cm.getCursor(), Pos(0, 2));
  1.1646 +  cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"});
  1.1647 +  sendKey(39);
  1.1648 +  eq(test, 55);
  1.1649 +  cm.removeKeyMap("mymap");
  1.1650 +  sendKey(39);
  1.1651 +  eqPos(cm.getCursor(), Pos(0, 3));
  1.1652 +}, {value: "abc"});
  1.1653 +
  1.1654 +testCM("findPosH", function(cm) {
  1.1655 +  forEach([{from: Pos(0, 0), to: Pos(0, 1), by: 1},
  1.1656 +           {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true},
  1.1657 +           {from: Pos(0, 0), to: Pos(0, 4), by: 1, unit: "word"},
  1.1658 +           {from: Pos(0, 0), to: Pos(0, 8), by: 2, unit: "word"},
  1.1659 +           {from: Pos(0, 0), to: Pos(2, 0), by: 20, unit: "word", hitSide: true},
  1.1660 +           {from: Pos(0, 7), to: Pos(0, 5), by: -1, unit: "word"},
  1.1661 +           {from: Pos(0, 4), to: Pos(0, 8), by: 1, unit: "word"},
  1.1662 +           {from: Pos(1, 0), to: Pos(1, 18), by: 3, unit: "word"},
  1.1663 +           {from: Pos(1, 22), to: Pos(1, 5), by: -3, unit: "word"},
  1.1664 +           {from: Pos(1, 15), to: Pos(1, 10), by: -5},
  1.1665 +           {from: Pos(1, 15), to: Pos(1, 10), by: -5, unit: "column"},
  1.1666 +           {from: Pos(1, 15), to: Pos(1, 0), by: -50, unit: "column", hitSide: true},
  1.1667 +           {from: Pos(1, 15), to: Pos(1, 24), by: 50, unit: "column", hitSide: true},
  1.1668 +           {from: Pos(1, 15), to: Pos(2, 0), by: 50, hitSide: true}], function(t) {
  1.1669 +    var r = cm.findPosH(t.from, t.by, t.unit || "char");
  1.1670 +    eqPos(r, t.to);
  1.1671 +    eq(!!r.hitSide, !!t.hitSide);
  1.1672 +  });
  1.1673 +}, {value: "line one\nline two.something.other\n"});
  1.1674 +
  1.1675 +testCM("beforeChange", function(cm) {
  1.1676 +  cm.on("beforeChange", function(cm, change) {
  1.1677 +    var text = [];
  1.1678 +    for (var i = 0; i < change.text.length; ++i)
  1.1679 +      text.push(change.text[i].replace(/\s/g, "_"));
  1.1680 +    change.update(null, null, text);
  1.1681 +  });
  1.1682 +  cm.setValue("hello, i am a\nnew document\n");
  1.1683 +  eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
  1.1684 +  CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) {
  1.1685 +    if (change.from.line == 0) change.cancel();
  1.1686 +  });
  1.1687 +  cm.setValue("oops"); // Canceled
  1.1688 +  eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
  1.1689 +  cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0));
  1.1690 +  eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey");
  1.1691 +}, {value: "abcdefghijk"});
  1.1692 +
  1.1693 +testCM("beforeChangeUndo", function(cm) {
  1.1694 +  cm.replaceRange("hi", Pos(0, 0), Pos(0));
  1.1695 +  cm.replaceRange("bye", Pos(0, 0), Pos(0));
  1.1696 +  eq(cm.historySize().undo, 2);
  1.1697 +  cm.on("beforeChange", function(cm, change) {
  1.1698 +    is(!change.update);
  1.1699 +    change.cancel();
  1.1700 +  });
  1.1701 +  cm.undo();
  1.1702 +  eq(cm.historySize().undo, 0);
  1.1703 +  eq(cm.getValue(), "bye\ntwo");
  1.1704 +}, {value: "one\ntwo"});
  1.1705 +
  1.1706 +testCM("beforeSelectionChange", function(cm) {
  1.1707 +  function notAtEnd(cm, pos) {
  1.1708 +    var len = cm.getLine(pos.line).length;
  1.1709 +    if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1);
  1.1710 +    return pos;
  1.1711 +  }
  1.1712 +  cm.on("beforeSelectionChange", function(cm, obj) {
  1.1713 +    obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor),
  1.1714 +                 head: notAtEnd(cm, obj.ranges[0].head)}]);
  1.1715 +  });
  1.1716 +
  1.1717 +  addDoc(cm, 10, 10);
  1.1718 +  cm.execCommand("goLineEnd");
  1.1719 +  eqPos(cm.getCursor(), Pos(0, 9));
  1.1720 +  cm.execCommand("selectAll");
  1.1721 +  eqPos(cm.getCursor("start"), Pos(0, 0));
  1.1722 +  eqPos(cm.getCursor("end"), Pos(9, 9));
  1.1723 +});
  1.1724 +
  1.1725 +testCM("change_removedText", function(cm) {
  1.1726 +  cm.setValue("abc\ndef");
  1.1727 +
  1.1728 +  var removedText = [];
  1.1729 +  cm.on("change", function(cm, change) {
  1.1730 +    removedText.push(change.removed);
  1.1731 +  });
  1.1732 +
  1.1733 +  cm.operation(function() {
  1.1734 +    cm.replaceRange("xyz", Pos(0, 0), Pos(1,1));
  1.1735 +    cm.replaceRange("123", Pos(0,0));
  1.1736 +  });
  1.1737 +
  1.1738 +  eq(removedText.length, 2);
  1.1739 +  eq(removedText[0].join("\n"), "abc\nd");
  1.1740 +  eq(removedText[1].join("\n"), "");
  1.1741 +
  1.1742 +  var removedText = [];
  1.1743 +  cm.undo();
  1.1744 +  eq(removedText.length, 2);
  1.1745 +  eq(removedText[0].join("\n"), "123");
  1.1746 +  eq(removedText[1].join("\n"), "xyz");
  1.1747 +
  1.1748 +  var removedText = [];
  1.1749 +  cm.redo();
  1.1750 +  eq(removedText.length, 2);
  1.1751 +  eq(removedText[0].join("\n"), "abc\nd");
  1.1752 +  eq(removedText[1].join("\n"), "");
  1.1753 +});
  1.1754 +
  1.1755 +testCM("lineStyleFromMode", function(cm) {
  1.1756 +  CodeMirror.defineMode("test_mode", function() {
  1.1757 +    return {token: function(stream) {
  1.1758 +      if (stream.match(/^\[[^\]]*\]/)) return "  line-brackets  ";
  1.1759 +      if (stream.match(/^\([^\)]*\)/)) return "  line-background-parens  ";
  1.1760 +      if (stream.match(/^<[^>]*>/)) return "  span  line-line  line-background-bg  ";
  1.1761 +      stream.match(/^\s+|^\S+/);
  1.1762 +    }};
  1.1763 +  });
  1.1764 +  cm.setOption("mode", "test_mode");
  1.1765 +  var bracketElts = byClassName(cm.getWrapperElement(), "brackets");
  1.1766 +  eq(bracketElts.length, 1, "brackets count");
  1.1767 +  eq(bracketElts[0].nodeName, "PRE");
  1.1768 +  is(!/brackets.*brackets/.test(bracketElts[0].className));
  1.1769 +  var parenElts = byClassName(cm.getWrapperElement(), "parens");
  1.1770 +  eq(parenElts.length, 1, "parens count");
  1.1771 +  eq(parenElts[0].nodeName, "DIV");
  1.1772 +  is(!/parens.*parens/.test(parenElts[0].className));
  1.1773 +  eq(parenElts[0].parentElement.nodeName, "DIV");
  1.1774 +
  1.1775 +  eq(byClassName(cm.getWrapperElement(), "bg").length, 1);
  1.1776 +  eq(byClassName(cm.getWrapperElement(), "line").length, 1);
  1.1777 +  var spanElts = byClassName(cm.getWrapperElement(), "cm-span");
  1.1778 +  eq(spanElts.length, 2);
  1.1779 +  is(/^\s*cm-span\s*$/.test(spanElts[0].className));
  1.1780 +}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: <tag> <tag>"});
  1.1781 +
  1.1782 +CodeMirror.registerHelper("xxx", "a", "A");
  1.1783 +CodeMirror.registerHelper("xxx", "b", "B");
  1.1784 +CodeMirror.defineMode("yyy", function() {
  1.1785 +  return {
  1.1786 +    token: function(stream) { stream.skipToEnd(); },
  1.1787 +    xxx: ["a", "b", "q"]
  1.1788 +  };
  1.1789 +});
  1.1790 +CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C");
  1.1791 +
  1.1792 +testCM("helpers", function(cm) {
  1.1793 +  cm.setOption("mode", "yyy");
  1.1794 +  eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B");
  1.1795 +  cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}});
  1.1796 +  eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C");
  1.1797 +  cm.setOption("mode", "javascript");
  1.1798 +  eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "");
  1.1799 +});
  1.1800 +
  1.1801 +testCM("selectionHistory", function(cm) {
  1.1802 +  for (var i = 0; i < 3; i++) {
  1.1803 +    cm.setExtending(true);
  1.1804 +    cm.execCommand("goCharRight");
  1.1805 +    cm.setExtending(false);
  1.1806 +    cm.execCommand("goCharRight");
  1.1807 +    cm.execCommand("goCharRight");
  1.1808 +  }
  1.1809 +  cm.execCommand("undoSelection");
  1.1810 +  eq(cm.getSelection(), "c");
  1.1811 +  cm.execCommand("undoSelection");
  1.1812 +  eq(cm.getSelection(), "");
  1.1813 +  eqPos(cm.getCursor(), Pos(0, 4));
  1.1814 +  cm.execCommand("undoSelection");
  1.1815 +  eq(cm.getSelection(), "b");
  1.1816 +  cm.execCommand("redoSelection");
  1.1817 +  eq(cm.getSelection(), "");
  1.1818 +  eqPos(cm.getCursor(), Pos(0, 4));
  1.1819 +  cm.execCommand("redoSelection");
  1.1820 +  eq(cm.getSelection(), "c");
  1.1821 +  cm.execCommand("redoSelection");
  1.1822 +  eq(cm.getSelection(), "");
  1.1823 +  eqPos(cm.getCursor(), Pos(0, 6));
  1.1824 +}, {value: "a b c d"});
  1.1825 +
  1.1826 +testCM("selectionChangeReducesRedo", function(cm) {
  1.1827 +  cm.replaceSelection("X");
  1.1828 +  cm.execCommand("goCharRight");
  1.1829 +  cm.undoSelection();
  1.1830 +  cm.execCommand("selectAll");
  1.1831 +  cm.undoSelection();
  1.1832 +  eq(cm.getValue(), "Xabc");
  1.1833 +  eqPos(cm.getCursor(), Pos(0, 1));
  1.1834 +  cm.undoSelection();
  1.1835 +  eq(cm.getValue(), "abc");
  1.1836 +}, {value: "abc"});
  1.1837 +
  1.1838 +testCM("selectionHistoryNonOverlapping", function(cm) {
  1.1839 +  cm.setSelection(Pos(0, 0), Pos(0, 1));
  1.1840 +  cm.setSelection(Pos(0, 2), Pos(0, 3));
  1.1841 +  cm.execCommand("undoSelection");
  1.1842 +  eqPos(cm.getCursor("anchor"), Pos(0, 0));
  1.1843 +  eqPos(cm.getCursor("head"), Pos(0, 1));
  1.1844 +}, {value: "1234"});
  1.1845 +
  1.1846 +testCM("cursorMotionSplitsHistory", function(cm) {
  1.1847 +  cm.replaceSelection("a");
  1.1848 +  cm.execCommand("goCharRight");
  1.1849 +  cm.replaceSelection("b");
  1.1850 +  cm.replaceSelection("c");
  1.1851 +  cm.undo();
  1.1852 +  eq(cm.getValue(), "a1234");
  1.1853 +  eqPos(cm.getCursor(), Pos(0, 2));
  1.1854 +  cm.undo();
  1.1855 +  eq(cm.getValue(), "1234");
  1.1856 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1857 +}, {value: "1234"});
  1.1858 +
  1.1859 +testCM("selChangeInOperationDoesNotSplit", function(cm) {
  1.1860 +  for (var i = 0; i < 4; i++) {
  1.1861 +    cm.operation(function() {
  1.1862 +      cm.replaceSelection("x");
  1.1863 +      cm.setCursor(Pos(0, cm.getCursor().ch - 1));
  1.1864 +    });
  1.1865 +  }
  1.1866 +  eqPos(cm.getCursor(), Pos(0, 0));
  1.1867 +  eq(cm.getValue(), "xxxxa");
  1.1868 +  cm.undo();
  1.1869 +  eq(cm.getValue(), "a");
  1.1870 +}, {value: "a"});
  1.1871 +
  1.1872 +testCM("alwaysMergeSelEventWithChangeOrigin", function(cm) {
  1.1873 +  cm.replaceSelection("U", null, "foo");
  1.1874 +  cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "foo"});
  1.1875 +  cm.undoSelection();
  1.1876 +  eq(cm.getValue(), "a");
  1.1877 +  cm.replaceSelection("V", null, "foo");
  1.1878 +  cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "bar"});
  1.1879 +  cm.undoSelection();
  1.1880 +  eq(cm.getValue(), "Va");
  1.1881 +}, {value: "a"});

mercurial