File modules/editor/codemirror/test/test.min.js

Last commit: Tue May 22 22:39:55 2018 +0200	Jan Dankert	Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.
1 var Pos = CodeMirror.Pos; 2 3 CodeMirror.defaults.rtlMoveVisually = true; 4 5 function forEach(arr, f) { 6 for (var i = 0, e = arr.length; i < e; ++i) f(arr[i], i); 7 } 8 9 function addDoc(cm, width, height) { 10 var content = [], line = ""; 11 for (var i = 0; i < width; ++i) line += "x"; 12 for (var i = 0; i < height; ++i) content.push(line); 13 cm.setValue(content.join("\n")); 14 } 15 16 function byClassName(elt, cls) { 17 if (elt.getElementsByClassName) return elt.getElementsByClassName(cls); 18 var found = [], re = new RegExp("\\b" + cls + "\\b"); 19 function search(elt) { 20 if (elt.nodeType == 3) return; 21 if (re.test(elt.className)) found.push(elt); 22 for (var i = 0, e = elt.childNodes.length; i < e; ++i) 23 search(elt.childNodes[i]); 24 } 25 search(elt); 26 return found; 27 } 28 29 var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); 30 var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); 31 var mac = /Mac/.test(navigator.platform); 32 var phantom = /PhantomJS/.test(navigator.userAgent); 33 var opera = /Opera\/\./.test(navigator.userAgent); 34 var opera_version = opera && navigator.userAgent.match(/Version\/(\d+\.\d+)/); 35 if (opera_version) opera_version = Number(opera_version); 36 var opera_lt10 = opera && (!opera_version || opera_version < 10); 37 38 namespace = "core_"; 39 40 test("core_fromTextArea", function() { 41 var te = document.getElementById("code"); 42 te.value = "CONTENT"; 43 var cm = CodeMirror.fromTextArea(te); 44 is(!te.offsetHeight); 45 eq(cm.getValue(), "CONTENT"); 46 cm.setValue("foo\nbar"); 47 eq(cm.getValue(), "foo\nbar"); 48 cm.save(); 49 is(/^foo\r?\nbar$/.test(te.value)); 50 cm.setValue("xxx"); 51 cm.toTextArea(); 52 is(te.offsetHeight); 53 eq(te.value, "xxx"); 54 }); 55 56 testCM("getRange", function(cm) { 57 eq(cm.getLine(0), "1234"); 58 eq(cm.getLine(1), "5678"); 59 eq(cm.getLine(2), null); 60 eq(cm.getLine(-1), null); 61 eq(cm.getRange(Pos(0, 0), Pos(0, 3)), "123"); 62 eq(cm.getRange(Pos(0, -1), Pos(0, 200)), "1234"); 63 eq(cm.getRange(Pos(0, 2), Pos(1, 2)), "34\n56"); 64 eq(cm.getRange(Pos(1, 2), Pos(100, 0)), "78"); 65 }, {value: "1234\n5678"}); 66 67 testCM("replaceRange", function(cm) { 68 eq(cm.getValue(), ""); 69 cm.replaceRange("foo\n", Pos(0, 0)); 70 eq(cm.getValue(), "foo\n"); 71 cm.replaceRange("a\nb", Pos(0, 1)); 72 eq(cm.getValue(), "fa\nboo\n"); 73 eq(cm.lineCount(), 3); 74 cm.replaceRange("xyzzy", Pos(0, 0), Pos(1, 1)); 75 eq(cm.getValue(), "xyzzyoo\n"); 76 cm.replaceRange("abc", Pos(0, 0), Pos(10, 0)); 77 eq(cm.getValue(), "abc"); 78 eq(cm.lineCount(), 1); 79 }); 80 81 testCM("selection", function(cm) { 82 cm.setSelection(Pos(0, 4), Pos(2, 2)); 83 is(cm.somethingSelected()); 84 eq(cm.getSelection(), "11\n222222\n33"); 85 eqCursorPos(cm.getCursor(false), Pos(2, 2)); 86 eqCursorPos(cm.getCursor(true), Pos(0, 4)); 87 cm.setSelection(Pos(1, 0)); 88 is(!cm.somethingSelected()); 89 eq(cm.getSelection(), ""); 90 eqCursorPos(cm.getCursor(true), Pos(1, 0)); 91 cm.replaceSelection("abc", "around"); 92 eq(cm.getSelection(), "abc"); 93 eq(cm.getValue(), "111111\nabc222222\n333333"); 94 cm.replaceSelection("def", "end"); 95 eq(cm.getSelection(), ""); 96 eqCursorPos(cm.getCursor(true), Pos(1, 3)); 97 cm.setCursor(Pos(2, 1)); 98 eqCursorPos(cm.getCursor(true), Pos(2, 1)); 99 cm.setCursor(1, 2); 100 eqCursorPos(cm.getCursor(true), Pos(1, 2)); 101 }, {value: "111111\n222222\n333333"}); 102 103 testCM("extendSelection", function(cm) { 104 cm.setExtending(true); 105 addDoc(cm, 10, 10); 106 cm.setSelection(Pos(3, 5)); 107 eqCursorPos(cm.getCursor("head"), Pos(3, 5)); 108 eqCursorPos(cm.getCursor("anchor"), Pos(3, 5)); 109 cm.setSelection(Pos(2, 5), Pos(5, 5)); 110 eqCursorPos(cm.getCursor("head"), Pos(5, 5)); 111 eqCursorPos(cm.getCursor("anchor"), Pos(2, 5)); 112 eqCursorPos(cm.getCursor("start"), Pos(2, 5)); 113 eqCursorPos(cm.getCursor("end"), Pos(5, 5)); 114 cm.setSelection(Pos(5, 5), Pos(2, 5)); 115 eqCursorPos(cm.getCursor("head"), Pos(2, 5)); 116 eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); 117 eqCursorPos(cm.getCursor("start"), Pos(2, 5)); 118 eqCursorPos(cm.getCursor("end"), Pos(5, 5)); 119 cm.extendSelection(Pos(3, 2)); 120 eqCursorPos(cm.getCursor("head"), Pos(3, 2)); 121 eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); 122 cm.extendSelection(Pos(6, 2)); 123 eqCursorPos(cm.getCursor("head"), Pos(6, 2)); 124 eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); 125 cm.extendSelection(Pos(6, 3), Pos(6, 4)); 126 eqCursorPos(cm.getCursor("head"), Pos(6, 4)); 127 eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); 128 cm.extendSelection(Pos(0, 3), Pos(0, 4)); 129 eqCursorPos(cm.getCursor("head"), Pos(0, 3)); 130 eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); 131 cm.extendSelection(Pos(4, 5), Pos(6, 5)); 132 eqCursorPos(cm.getCursor("head"), Pos(6, 5)); 133 eqCursorPos(cm.getCursor("anchor"), Pos(4, 5)); 134 cm.setExtending(false); 135 cm.extendSelection(Pos(0, 3), Pos(0, 4)); 136 eqCursorPos(cm.getCursor("head"), Pos(0, 3)); 137 eqCursorPos(cm.getCursor("anchor"), Pos(0, 4)); 138 }); 139 140 testCM("lines", function(cm) { 141 eq(cm.getLine(0), "111111"); 142 eq(cm.getLine(1), "222222"); 143 eq(cm.getLine(-1), null); 144 cm.replaceRange("", Pos(1, 0), Pos(2, 0)) 145 cm.replaceRange("abc", Pos(1, 0), Pos(1)); 146 eq(cm.getValue(), "111111\nabc"); 147 }, {value: "111111\n222222\n333333"}); 148 149 testCM("indent", function(cm) { 150 cm.indentLine(1); 151 eq(cm.getLine(1), " blah();"); 152 cm.setOption("indentUnit", 8); 153 cm.indentLine(1); 154 eq(cm.getLine(1), "\tblah();"); 155 cm.setOption("indentUnit", 10); 156 cm.setOption("tabSize", 4); 157 cm.indentLine(1); 158 eq(cm.getLine(1), "\t\t blah();"); 159 }, {value: "if (x) {\nblah();\n}", indentUnit: 3, indentWithTabs: true, tabSize: 8}); 160 161 testCM("indentByNumber", function(cm) { 162 cm.indentLine(0, 2); 163 eq(cm.getLine(0), " foo"); 164 cm.indentLine(0, -200); 165 eq(cm.getLine(0), "foo"); 166 cm.setSelection(Pos(0, 0), Pos(1, 2)); 167 cm.indentSelection(3); 168 eq(cm.getValue(), " foo\n bar\nbaz"); 169 }, {value: "foo\nbar\nbaz"}); 170 171 test("core_defaults", function() { 172 var defsCopy = {}, defs = CodeMirror.defaults; 173 for (var opt in defs) defsCopy[opt] = defs[opt]; 174 defs.indentUnit = 5; 175 defs.value = "uu"; 176 defs.indentWithTabs = true; 177 defs.tabindex = 55; 178 var place = document.getElementById("testground"), cm = CodeMirror(place); 179 try { 180 eq(cm.getOption("indentUnit"), 5); 181 cm.setOption("indentUnit", 10); 182 eq(defs.indentUnit, 5); 183 eq(cm.getValue(), "uu"); 184 eq(cm.getOption("indentWithTabs"), true); 185 eq(cm.getInputField().tabIndex, 55); 186 } 187 finally { 188 for (var opt in defsCopy) defs[opt] = defsCopy[opt]; 189 place.removeChild(cm.getWrapperElement()); 190 } 191 }); 192 193 testCM("lineInfo", function(cm) { 194 eq(cm.lineInfo(-1), null); 195 var mark = document.createElement("span"); 196 var lh = cm.setGutterMarker(1, "FOO", mark); 197 var info = cm.lineInfo(1); 198 eq(info.text, "222222"); 199 eq(info.gutterMarkers.FOO, mark); 200 eq(info.line, 1); 201 eq(cm.lineInfo(2).gutterMarkers, null); 202 cm.setGutterMarker(lh, "FOO", null); 203 eq(cm.lineInfo(1).gutterMarkers, null); 204 cm.setGutterMarker(1, "FOO", mark); 205 cm.setGutterMarker(0, "FOO", mark); 206 cm.clearGutter("FOO"); 207 eq(cm.lineInfo(0).gutterMarkers, null); 208 eq(cm.lineInfo(1).gutterMarkers, null); 209 }, {value: "111111\n222222\n333333"}); 210 211 testCM("coords", function(cm) { 212 cm.setSize(null, 100); 213 addDoc(cm, 32, 200); 214 var top = cm.charCoords(Pos(0, 0)); 215 var bot = cm.charCoords(Pos(200, 30)); 216 is(top.left < bot.left); 217 is(top.top < bot.top); 218 is(top.top < top.bottom); 219 cm.scrollTo(null, 100); 220 var top2 = cm.charCoords(Pos(0, 0)); 221 is(top.top > top2.top); 222 eq(top.left, top2.left); 223 }); 224 225 testCM("coordsChar", function(cm) { 226 addDoc(cm, 35, 70); 227 for (var i = 0; i < 2; ++i) { 228 var sys = i ? "local" : "page"; 229 for (var ch = 0; ch <= 35; ch += 5) { 230 for (var line = 0; line < 70; line += 5) { 231 cm.setCursor(line, ch); 232 var coords = cm.charCoords(Pos(line, ch), sys); 233 var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys); 234 eqCharPos(pos, Pos(line, ch)); 235 } 236 } 237 } 238 }, {lineNumbers: true}); 239 240 testCM("coordsCharBidi", function(cm) { 241 addDoc(cm, 35, 70); 242 // Put an rtl character into each line to trigger the bidi code path in coordsChar 243 cm.setValue(cm.getValue().replace(/\bx/g, 'و')) 244 for (var i = 0; i < 2; ++i) { 245 var sys = i ? "local" : "page"; 246 for (var ch = 2; ch <= 35; ch += 5) { 247 for (var line = 0; line < 70; line += 5) { 248 cm.setCursor(line, ch); 249 var coords = cm.charCoords(Pos(line, ch), sys); 250 var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys); 251 eqCharPos(pos, Pos(line, ch)); 252 } 253 } 254 } 255 }, {lineNumbers: true}); 256 257 testCM("badBidiOptimization", function(cm) { 258 var coords = cm.charCoords(Pos(0, 34)) 259 eqCharPos(cm.coordsChar({left: coords.right, top: coords.top + 2}), Pos(0, 34)) 260 }, {value: "----------<p class=\"title\">هل يمكنك اختيار مستوى قسط التأمين الذي ترغب بدفعه؟</p>"}) 261 262 testCM("posFromIndex", function(cm) { 263 cm.setValue( 264 "This function should\n" + 265 "convert a zero based index\n" + 266 "to line and ch." 267 ); 268 269 var examples = [ 270 { index: -1, line: 0, ch: 0 }, // <- Tests clipping 271 { index: 0, line: 0, ch: 0 }, 272 { index: 10, line: 0, ch: 10 }, 273 { index: 39, line: 1, ch: 18 }, 274 { index: 55, line: 2, ch: 7 }, 275 { index: 63, line: 2, ch: 15 }, 276 { index: 64, line: 2, ch: 15 } // <- Tests clipping 277 ]; 278 279 for (var i = 0; i < examples.length; i++) { 280 var example = examples[i]; 281 var pos = cm.posFromIndex(example.index); 282 eq(pos.line, example.line); 283 eq(pos.ch, example.ch); 284 if (example.index >= 0 && example.index < 64) 285 eq(cm.indexFromPos(pos), example.index); 286 } 287 }); 288 289 testCM("undo", function(cm) { 290 cm.replaceRange("def", Pos(0, 0), Pos(0)); 291 eq(cm.historySize().undo, 1); 292 cm.undo(); 293 eq(cm.getValue(), "abc"); 294 eq(cm.historySize().undo, 0); 295 eq(cm.historySize().redo, 1); 296 cm.redo(); 297 eq(cm.getValue(), "def"); 298 eq(cm.historySize().undo, 1); 299 eq(cm.historySize().redo, 0); 300 cm.setValue("1\n\n\n2"); 301 cm.clearHistory(); 302 eq(cm.historySize().undo, 0); 303 for (var i = 0; i < 20; ++i) { 304 cm.replaceRange("a", Pos(0, 0)); 305 cm.replaceRange("b", Pos(3, 0)); 306 } 307 eq(cm.historySize().undo, 40); 308 for (var i = 0; i < 40; ++i) 309 cm.undo(); 310 eq(cm.historySize().redo, 40); 311 eq(cm.getValue(), "1\n\n\n2"); 312 }, {value: "abc"}); 313 314 testCM("undoDepth", function(cm) { 315 cm.replaceRange("d", Pos(0)); 316 cm.replaceRange("e", Pos(0)); 317 cm.replaceRange("f", Pos(0)); 318 cm.undo(); cm.undo(); cm.undo(); 319 eq(cm.getValue(), "abcd"); 320 }, {value: "abc", undoDepth: 4}); 321 322 testCM("undoDoesntClearValue", function(cm) { 323 cm.undo(); 324 eq(cm.getValue(), "x"); 325 }, {value: "x"}); 326 327 testCM("undoMultiLine", function(cm) { 328 cm.operation(function() { 329 cm.replaceRange("x", Pos(0, 0)); 330 cm.replaceRange("y", Pos(1, 0)); 331 }); 332 cm.undo(); 333 eq(cm.getValue(), "abc\ndef\nghi"); 334 cm.operation(function() { 335 cm.replaceRange("y", Pos(1, 0)); 336 cm.replaceRange("x", Pos(0, 0)); 337 }); 338 cm.undo(); 339 eq(cm.getValue(), "abc\ndef\nghi"); 340 cm.operation(function() { 341 cm.replaceRange("y", Pos(2, 0)); 342 cm.replaceRange("x", Pos(1, 0)); 343 cm.replaceRange("z", Pos(2, 0)); 344 }); 345 cm.undo(); 346 eq(cm.getValue(), "abc\ndef\nghi", 3); 347 }, {value: "abc\ndef\nghi"}); 348 349 testCM("undoComposite", function(cm) { 350 cm.replaceRange("y", Pos(1)); 351 cm.operation(function() { 352 cm.replaceRange("x", Pos(0)); 353 cm.replaceRange("z", Pos(2)); 354 }); 355 eq(cm.getValue(), "ax\nby\ncz\n"); 356 cm.undo(); 357 eq(cm.getValue(), "a\nby\nc\n"); 358 cm.undo(); 359 eq(cm.getValue(), "a\nb\nc\n"); 360 cm.redo(); cm.redo(); 361 eq(cm.getValue(), "ax\nby\ncz\n"); 362 }, {value: "a\nb\nc\n"}); 363 364 testCM("undoSelection", function(cm) { 365 cm.setSelection(Pos(0, 2), Pos(0, 4)); 366 cm.replaceSelection(""); 367 cm.setCursor(Pos(1, 0)); 368 cm.undo(); 369 eqCursorPos(cm.getCursor(true), Pos(0, 2)); 370 eqCursorPos(cm.getCursor(false), Pos(0, 4)); 371 cm.setCursor(Pos(1, 0)); 372 cm.redo(); 373 eqCursorPos(cm.getCursor(true), Pos(0, 2)); 374 eqCursorPos(cm.getCursor(false), Pos(0, 2)); 375 }, {value: "abcdefgh\n"}); 376 377 testCM("undoSelectionAsBefore", function(cm) { 378 cm.replaceSelection("abc", "around"); 379 cm.undo(); 380 cm.redo(); 381 eq(cm.getSelection(), "abc"); 382 }); 383 384 testCM("selectionChangeConfusesHistory", function(cm) { 385 cm.replaceSelection("abc", null, "dontmerge"); 386 cm.operation(function() { 387 cm.setCursor(Pos(0, 0)); 388 cm.replaceSelection("abc", null, "dontmerge"); 389 }); 390 eq(cm.historySize().undo, 2); 391 }); 392 393 testCM("markTextSingleLine", function(cm) { 394 forEach([{a: 0, b: 1, c: "", f: 2, t: 5}, 395 {a: 0, b: 4, c: "", f: 0, t: 2}, 396 {a: 1, b: 2, c: "x", f: 3, t: 6}, 397 {a: 4, b: 5, c: "", f: 3, t: 5}, 398 {a: 4, b: 5, c: "xx", f: 3, t: 7}, 399 {a: 2, b: 5, c: "", f: 2, t: 3}, 400 {a: 2, b: 5, c: "abcd", f: 6, t: 7}, 401 {a: 2, b: 6, c: "x", f: null, t: null}, 402 {a: 3, b: 6, c: "", f: null, t: null}, 403 {a: 0, b: 9, c: "hallo", f: null, t: null}, 404 {a: 4, b: 6, c: "x", f: 3, t: 4}, 405 {a: 4, b: 8, c: "", f: 3, t: 4}, 406 {a: 6, b: 6, c: "a", f: 3, t: 6}, 407 {a: 8, b: 9, c: "", f: 3, t: 6}], function(test) { 408 cm.setValue("1234567890"); 409 var r = cm.markText(Pos(0, 3), Pos(0, 6), {className: "foo"}); 410 cm.replaceRange(test.c, Pos(0, test.a), Pos(0, test.b)); 411 var f = r.find(); 412 eq(f && f.from.ch, test.f); eq(f && f.to.ch, test.t); 413 }); 414 }); 415 416 testCM("markTextMultiLine", function(cm) { 417 function p(v) { return v && Pos(v[0], v[1]); } 418 forEach([{a: [0, 0], b: [0, 5], c: "", f: [0, 0], t: [2, 5]}, 419 {a: [0, 0], b: [0, 5], c: "foo\n", f: [1, 0], t: [3, 5]}, 420 {a: [0, 1], b: [0, 10], c: "", f: [0, 1], t: [2, 5]}, 421 {a: [0, 5], b: [0, 6], c: "x", f: [0, 6], t: [2, 5]}, 422 {a: [0, 0], b: [1, 0], c: "", f: [0, 0], t: [1, 5]}, 423 {a: [0, 6], b: [2, 4], c: "", f: [0, 5], t: [0, 7]}, 424 {a: [0, 6], b: [2, 4], c: "aa", f: [0, 5], t: [0, 9]}, 425 {a: [1, 2], b: [1, 8], c: "", f: [0, 5], t: [2, 5]}, 426 {a: [0, 5], b: [2, 5], c: "xx", f: null, t: null}, 427 {a: [0, 0], b: [2, 10], c: "x", f: null, t: null}, 428 {a: [1, 5], b: [2, 5], c: "", f: [0, 5], t: [1, 5]}, 429 {a: [2, 0], b: [2, 3], c: "", f: [0, 5], t: [2, 2]}, 430 {a: [2, 5], b: [3, 0], c: "a\nb", f: [0, 5], t: [2, 5]}, 431 {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 3]}, 432 {a: [1, 1], b: [1, 9], c: "1\n2\n3", f: [0, 5], t: [4, 5]}], function(test) { 433 cm.setValue("aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\ndddddddd\n"); 434 var r = cm.markText(Pos(0, 5), Pos(2, 5), 435 {className: "CodeMirror-matchingbracket"}); 436 cm.replaceRange(test.c, p(test.a), p(test.b)); 437 var f = r.find(); 438 eqCursorPos(f && f.from, p(test.f)); eqCursorPos(f && f.to, p(test.t)); 439 }); 440 }); 441 442 testCM("markTextUndo", function(cm) { 443 var marker1, marker2, bookmark; 444 marker1 = cm.markText(Pos(0, 1), Pos(0, 3), 445 {className: "CodeMirror-matchingbracket"}); 446 marker2 = cm.markText(Pos(0, 0), Pos(2, 1), 447 {className: "CodeMirror-matchingbracket"}); 448 bookmark = cm.setBookmark(Pos(1, 5)); 449 cm.operation(function(){ 450 cm.replaceRange("foo", Pos(0, 2)); 451 cm.replaceRange("bar\nbaz\nbug\n", Pos(2, 0), Pos(3, 0)); 452 }); 453 var v1 = cm.getValue(); 454 cm.setValue(""); 455 eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null); 456 cm.undo(); 457 eqCursorPos(bookmark.find(), Pos(1, 5), "still there"); 458 cm.undo(); 459 var m1Pos = marker1.find(), m2Pos = marker2.find(); 460 eqCursorPos(m1Pos.from, Pos(0, 1)); eqCursorPos(m1Pos.to, Pos(0, 3)); 461 eqCursorPos(m2Pos.from, Pos(0, 0)); eqCursorPos(m2Pos.to, Pos(2, 1)); 462 eqCursorPos(bookmark.find(), Pos(1, 5)); 463 cm.redo(); cm.redo(); 464 eq(bookmark.find(), null); 465 cm.undo(); 466 eqCursorPos(bookmark.find(), Pos(1, 5)); 467 eq(cm.getValue(), v1); 468 }, {value: "1234\n56789\n00\n"}); 469 470 testCM("markTextStayGone", function(cm) { 471 var m1 = cm.markText(Pos(0, 0), Pos(0, 1)); 472 cm.replaceRange("hi", Pos(0, 2)); 473 m1.clear(); 474 cm.undo(); 475 eq(m1.find(), null); 476 }, {value: "hello"}); 477 478 testCM("markTextAllowEmpty", function(cm) { 479 var m1 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false}); 480 is(m1.find()); 481 cm.replaceRange("x", Pos(0, 0)); 482 is(m1.find()); 483 cm.replaceRange("y", Pos(0, 2)); 484 is(m1.find()); 485 cm.replaceRange("z", Pos(0, 3), Pos(0, 4)); 486 is(!m1.find()); 487 var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false, 488 inclusiveLeft: true, 489 inclusiveRight: true}); 490 cm.replaceRange("q", Pos(0, 1), Pos(0, 2)); 491 is(m2.find()); 492 cm.replaceRange("", Pos(0, 0), Pos(0, 3)); 493 is(!m2.find()); 494 var m3 = cm.markText(Pos(0, 1), Pos(0, 1), {clearWhenEmpty: false}); 495 cm.replaceRange("a", Pos(0, 3)); 496 is(m3.find()); 497 cm.replaceRange("b", Pos(0, 1)); 498 is(!m3.find()); 499 }, {value: "abcde"}); 500 501 testCM("markTextStacked", function(cm) { 502 var m1 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false}); 503 var m2 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false}); 504 cm.replaceRange("B", Pos(0, 1)); 505 is(m1.find() && m2.find()); 506 }, {value: "A"}); 507 508 testCM("undoPreservesNewMarks", function(cm) { 509 cm.markText(Pos(0, 3), Pos(0, 4)); 510 cm.markText(Pos(1, 1), Pos(1, 3)); 511 cm.replaceRange("", Pos(0, 3), Pos(3, 1)); 512 var mBefore = cm.markText(Pos(0, 0), Pos(0, 1)); 513 var mAfter = cm.markText(Pos(0, 5), Pos(0, 6)); 514 var mAround = cm.markText(Pos(0, 2), Pos(0, 4)); 515 cm.undo(); 516 eqCursorPos(mBefore.find().from, Pos(0, 0)); 517 eqCursorPos(mBefore.find().to, Pos(0, 1)); 518 eqCursorPos(mAfter.find().from, Pos(3, 3)); 519 eqCursorPos(mAfter.find().to, Pos(3, 4)); 520 eqCursorPos(mAround.find().from, Pos(0, 2)); 521 eqCursorPos(mAround.find().to, Pos(3, 2)); 522 var found = cm.findMarksAt(Pos(2, 2)); 523 eq(found.length, 1); 524 eq(found[0], mAround); 525 }, {value: "aaaa\nbbbb\ncccc\ndddd"}); 526 527 testCM("markClearBetween", function(cm) { 528 cm.setValue("aaa\nbbb\nccc\nddd\n"); 529 cm.markText(Pos(0, 0), Pos(2)); 530 cm.replaceRange("aaa\nbbb\nccc", Pos(0, 0), Pos(2)); 531 eq(cm.findMarksAt(Pos(1, 1)).length, 0); 532 }); 533 534 testCM("findMarksMiddle", function(cm) { 535 var mark = cm.markText(Pos(1, 1), Pos(3, 1)); 536 var found = cm.findMarks(Pos(2, 1), Pos(2, 2)); 537 eq(found.length, 1); 538 eq(found[0], mark); 539 }, {value: "line 0\nline 1\nline 2\nline 3"}); 540 541 testCM("deleteSpanCollapsedInclusiveLeft", function(cm) { 542 var from = Pos(1, 0), to = Pos(1, 1); 543 var m = cm.markText(from, to, {collapsed: true, inclusiveLeft: true}); 544 // Delete collapsed span. 545 cm.replaceRange("", from, to); 546 }, {value: "abc\nX\ndef"}); 547 548 testCM("markTextCSS", function(cm) { 549 function present() { 550 var spans = cm.display.lineDiv.getElementsByTagName("span"); 551 for (var i = 0; i < spans.length; i++) 552 if (spans[i].style.color == "cyan" && span[i].textContent == "cdefg") return true; 553 } 554 var m = cm.markText(Pos(0, 2), Pos(0, 6), {css: "color: cyan"}); 555 m.clear(); 556 is(!present()); 557 }, {value: "abcdefgh"}); 558 559 testCM("bookmark", function(cm) { 560 function p(v) { return v && Pos(v[0], v[1]); } 561 forEach([{a: [1, 0], b: [1, 1], c: "", d: [1, 4]}, 562 {a: [1, 1], b: [1, 1], c: "xx", d: [1, 7]}, 563 {a: [1, 4], b: [1, 5], c: "ab", d: [1, 6]}, 564 {a: [1, 4], b: [1, 6], c: "", d: null}, 565 {a: [1, 5], b: [1, 6], c: "abc", d: [1, 5]}, 566 {a: [1, 6], b: [1, 8], c: "", d: [1, 5]}, 567 {a: [1, 4], b: [1, 4], c: "\n\n", d: [3, 1]}, 568 {bm: [1, 9], a: [1, 1], b: [1, 1], c: "\n", d: [2, 8]}], function(test) { 569 cm.setValue("1234567890\n1234567890\n1234567890"); 570 var b = cm.setBookmark(p(test.bm) || Pos(1, 5)); 571 cm.replaceRange(test.c, p(test.a), p(test.b)); 572 eqCursorPos(b.find(), p(test.d)); 573 }); 574 }); 575 576 testCM("bookmarkInsertLeft", function(cm) { 577 var br = cm.setBookmark(Pos(0, 2), {insertLeft: false}); 578 var bl = cm.setBookmark(Pos(0, 2), {insertLeft: true}); 579 cm.setCursor(Pos(0, 2)); 580 cm.replaceSelection("hi"); 581 eqCursorPos(br.find(), Pos(0, 2)); 582 eqCursorPos(bl.find(), Pos(0, 4)); 583 cm.replaceRange("", Pos(0, 4), Pos(0, 5)); 584 cm.replaceRange("", Pos(0, 2), Pos(0, 4)); 585 cm.replaceRange("", Pos(0, 1), Pos(0, 2)); 586 // Verify that deleting next to bookmarks doesn't kill them 587 eqCursorPos(br.find(), Pos(0, 1)); 588 eqCursorPos(bl.find(), Pos(0, 1)); 589 }, {value: "abcdef"}); 590 591 testCM("bookmarkCursor", function(cm) { 592 var pos01 = cm.cursorCoords(Pos(0, 1)), pos11 = cm.cursorCoords(Pos(1, 1)), 593 pos20 = cm.cursorCoords(Pos(2, 0)), pos30 = cm.cursorCoords(Pos(3, 0)), 594 pos41 = cm.cursorCoords(Pos(4, 1)); 595 cm.setBookmark(Pos(0, 1), {widget: document.createTextNode("←"), insertLeft: true}); 596 cm.setBookmark(Pos(2, 0), {widget: document.createTextNode("←"), insertLeft: true}); 597 cm.setBookmark(Pos(1, 1), {widget: document.createTextNode("→")}); 598 cm.setBookmark(Pos(3, 0), {widget: document.createTextNode("→")}); 599 var new01 = cm.cursorCoords(Pos(0, 1)), new11 = cm.cursorCoords(Pos(1, 1)), 600 new20 = cm.cursorCoords(Pos(2, 0)), new30 = cm.cursorCoords(Pos(3, 0)); 601 near(new01.left, pos01.left, 1); 602 near(new01.top, pos01.top, 1); 603 is(new11.left > pos11.left, "at right, middle of line"); 604 near(new11.top == pos11.top, 1); 605 near(new20.left, pos20.left, 1); 606 near(new20.top, pos20.top, 1); 607 is(new30.left > pos30.left, "at right, empty line"); 608 near(new30.top, pos30, 1); 609 cm.setBookmark(Pos(4, 0), {widget: document.createTextNode("→")}); 610 is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug"); 611 }, {value: "foo\nbar\n\n\nx\ny"}); 612 613 testCM("multiBookmarkCursor", function(cm) { 614 if (phantom) return; 615 var ms = [], m; 616 function add(insertLeft) { 617 for (var i = 0; i < 3; ++i) { 618 var node = document.createElement("span"); 619 node.innerHTML = "X"; 620 ms.push(cm.setBookmark(Pos(0, 1), {widget: node, insertLeft: insertLeft})); 621 } 622 } 623 var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left; 624 add(true); 625 near(base1, cm.cursorCoords(Pos(0, 1)).left, 1); 626 while (m = ms.pop()) m.clear(); 627 add(false); 628 near(base4, cm.cursorCoords(Pos(0, 1)).left, 1); 629 }, {value: "abcdefg"}); 630 631 testCM("getAllMarks", function(cm) { 632 addDoc(cm, 10, 10); 633 var m1 = cm.setBookmark(Pos(0, 2)); 634 var m2 = cm.markText(Pos(0, 2), Pos(3, 2)); 635 var m3 = cm.markText(Pos(1, 2), Pos(1, 8)); 636 var m4 = cm.markText(Pos(8, 0), Pos(9, 0)); 637 eq(cm.getAllMarks().length, 4); 638 m1.clear(); 639 m3.clear(); 640 eq(cm.getAllMarks().length, 2); 641 }); 642 643 testCM("setValueClears", function(cm) { 644 cm.addLineClass(0, "wrap", "foo"); 645 var mark = cm.markText(Pos(0, 0), Pos(1, 1), {inclusiveLeft: true, inclusiveRight: true}); 646 cm.setValue("foo"); 647 is(!cm.lineInfo(0).wrapClass); 648 is(!mark.find()); 649 }, {value: "a\nb"}); 650 651 testCM("bug577", function(cm) { 652 cm.setValue("a\nb"); 653 cm.clearHistory(); 654 cm.setValue("fooooo"); 655 cm.undo(); 656 }); 657 658 testCM("scrollSnap", function(cm) { 659 cm.setSize(100, 100); 660 addDoc(cm, 200, 200); 661 cm.setCursor(Pos(100, 180)); 662 var info = cm.getScrollInfo(); 663 is(info.left > 0 && info.top > 0); 664 cm.setCursor(Pos(0, 0)); 665 info = cm.getScrollInfo(); 666 is(info.left == 0 && info.top == 0, "scrolled clean to top"); 667 cm.setCursor(Pos(100, 180)); 668 cm.setCursor(Pos(199, 0)); 669 info = cm.getScrollInfo(); 670 is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom"); 671 }); 672 673 testCM("scrollIntoView", function(cm) { 674 if (phantom) return; 675 function test(line, ch, msg) { 676 var pos = Pos(line, ch); 677 cm.scrollIntoView(pos); 678 var outer = cm.getWrapperElement().getBoundingClientRect(); 679 var box = cm.charCoords(pos, "window"); 680 is(box.left >= outer.left, msg + " (left)"); 681 is(box.right <= outer.right, msg + " (right)"); 682 is(box.top >= outer.top, msg + " (top)"); 683 is(box.bottom <= outer.bottom, msg + " (bottom)"); 684 } 685 addDoc(cm, 200, 200); 686 test(199, 199, "bottom right"); 687 test(0, 0, "top left"); 688 test(100, 100, "center"); 689 test(199, 0, "bottom left"); 690 test(0, 199, "top right"); 691 test(100, 100, "center again"); 692 }); 693 694 testCM("scrollBackAndForth", function(cm) { 695 addDoc(cm, 1, 200); 696 cm.operation(function() { 697 cm.scrollIntoView(Pos(199, 0)); 698 cm.scrollIntoView(Pos(4, 0)); 699 }); 700 is(cm.getScrollInfo().top > 0); 701 }); 702 703 testCM("selectAllNoScroll", function(cm) { 704 addDoc(cm, 1, 200); 705 cm.execCommand("selectAll"); 706 eq(cm.getScrollInfo().top, 0); 707 cm.setCursor(199); 708 cm.execCommand("selectAll"); 709 is(cm.getScrollInfo().top > 0); 710 }); 711 712 testCM("selectionPos", function(cm) { 713 if (phantom || cm.getOption("inputStyle") != "textarea") return; 714 cm.setSize(100, 100); 715 addDoc(cm, 200, 100); 716 cm.setSelection(Pos(1, 100), Pos(98, 100)); 717 var lineWidth = cm.charCoords(Pos(0, 200), "local").left; 718 var lineHeight = (cm.charCoords(Pos(99)).top - cm.charCoords(Pos(0)).top) / 100; 719 cm.scrollTo(0, 0); 720 var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected"); 721 var outer = cm.getWrapperElement().getBoundingClientRect(); 722 var sawMiddle, sawTop, sawBottom; 723 for (var i = 0, e = selElt.length; i < e; ++i) { 724 var box = selElt[i].getBoundingClientRect(); 725 var atLeft = box.left - outer.left < 30; 726 var width = box.right - box.left; 727 var atRight = box.right - outer.left > .8 * lineWidth; 728 if (atLeft && atRight) { 729 sawMiddle = true; 730 is(box.bottom - box.top > 90 * lineHeight, "middle high"); 731 is(width > .9 * lineWidth, "middle wide"); 732 } else { 733 is(width > .4 * lineWidth, "top/bot wide enough"); 734 is(width < .6 * lineWidth, "top/bot slim enough"); 735 if (atLeft) { 736 sawBottom = true; 737 is(box.top - outer.top > 96 * lineHeight, "bot below"); 738 } else if (atRight) { 739 sawTop = true; 740 is(box.top - outer.top < 2.1 * lineHeight, "top above"); 741 } 742 } 743 } 744 is(sawTop && sawBottom && sawMiddle, "all parts"); 745 }, null); 746 747 testCM("restoreHistory", function(cm) { 748 cm.setValue("abc\ndef"); 749 cm.replaceRange("hello", Pos(1, 0), Pos(1)); 750 cm.replaceRange("goop", Pos(0, 0), Pos(0)); 751 cm.undo(); 752 var storedVal = cm.getValue(), storedHist = cm.getHistory(); 753 if (window.JSON) storedHist = JSON.parse(JSON.stringify(storedHist)); 754 eq(storedVal, "abc\nhello"); 755 cm.setValue(""); 756 cm.clearHistory(); 757 eq(cm.historySize().undo, 0); 758 cm.setValue(storedVal); 759 cm.setHistory(storedHist); 760 cm.redo(); 761 eq(cm.getValue(), "goop\nhello"); 762 cm.undo(); cm.undo(); 763 eq(cm.getValue(), "abc\ndef"); 764 }); 765 766 testCM("doubleScrollbar", function(cm) { 767 var dummy = document.body.appendChild(document.createElement("p")); 768 dummy.style.cssText = "height: 50px; overflow: scroll; width: 50px"; 769 var scrollbarWidth = dummy.offsetWidth + 1 - dummy.clientWidth; 770 document.body.removeChild(dummy); 771 if (scrollbarWidth < 2) return; 772 cm.setSize(null, 100); 773 addDoc(cm, 1, 300); 774 var wrap = cm.getWrapperElement(); 775 is(wrap.offsetWidth - byClassName(wrap, "CodeMirror-lines")[0].offsetWidth <= scrollbarWidth * 1.5); 776 }); 777 778 testCM("weirdLinebreaks", function(cm) { 779 cm.setValue("foo\nbar\rbaz\r\nquux\n\rplop"); 780 is(cm.getValue(), "foo\nbar\nbaz\nquux\n\nplop"); 781 is(cm.lineCount(), 6); 782 cm.setValue("\n\n"); 783 is(cm.lineCount(), 3); 784 }); 785 786 testCM("setSize", function(cm) { 787 cm.setSize(100, 100); 788 var wrap = cm.getWrapperElement(); 789 is(wrap.offsetWidth, 100); 790 is(wrap.offsetHeight, 100); 791 cm.setSize("100%", "3em"); 792 is(wrap.style.width, "100%"); 793 is(wrap.style.height, "3em"); 794 cm.setSize(null, 40); 795 is(wrap.style.width, "100%"); 796 is(wrap.style.height, "40px"); 797 }); 798 799 function foldLines(cm, start, end, autoClear) { 800 return cm.markText(Pos(start, 0), Pos(end - 1), { 801 inclusiveLeft: true, 802 inclusiveRight: true, 803 collapsed: true, 804 clearOnEnter: autoClear 805 }); 806 } 807 808 testCM("collapsedLines", function(cm) { 809 addDoc(cm, 4, 10); 810 var range = foldLines(cm, 4, 5), cleared = 0; 811 CodeMirror.on(range, "clear", function() {cleared++;}); 812 cm.setCursor(Pos(3, 0)); 813 CodeMirror.commands.goLineDown(cm); 814 eqCharPos(cm.getCursor(), Pos(5, 0)); 815 cm.replaceRange("abcdefg", Pos(3, 0), Pos(3)); 816 cm.setCursor(Pos(3, 6)); 817 CodeMirror.commands.goLineDown(cm); 818 eqCharPos(cm.getCursor(), Pos(5, 4)); 819 cm.replaceRange("ab", Pos(3, 0), Pos(3)); 820 cm.setCursor(Pos(3, 2)); 821 CodeMirror.commands.goLineDown(cm); 822 eqCharPos(cm.getCursor(), Pos(5, 2)); 823 cm.operation(function() {range.clear(); range.clear();}); 824 eq(cleared, 1); 825 }); 826 827 testCM("collapsedRangeCoordsChar", function(cm) { 828 var pos_1_3 = cm.charCoords(Pos(1, 3)); 829 pos_1_3.left += 2; pos_1_3.top += 2; 830 var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true}; 831 var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts); 832 eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); 833 m1.clear(); 834 var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true}); 835 var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true}); 836 eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); 837 m1.clear(); m2.clear(); 838 var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts); 839 eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); 840 }, {value: "123456\nabcdef\nghijkl\nmnopqr\n"}); 841 842 testCM("collapsedRangeBetweenLinesSelected", function(cm) { 843 if (cm.getOption("inputStyle") != "textarea") return; 844 var widget = document.createElement("span"); 845 widget.textContent = "\u2194"; 846 cm.markText(Pos(0, 3), Pos(1, 0), {replacedWith: widget}); 847 cm.setSelection(Pos(0, 3), Pos(1, 0)); 848 var selElts = byClassName(cm.getWrapperElement(), "CodeMirror-selected"); 849 for (var i = 0, w = 0; i < selElts.length; i++) 850 w += selElts[i].offsetWidth; 851 is(w > 0); 852 }, {value: "one\ntwo"}); 853 854 testCM("randomCollapsedRanges", function(cm) { 855 addDoc(cm, 20, 500); 856 cm.operation(function() { 857 for (var i = 0; i < 200; i++) { 858 var start = Pos(Math.floor(Math.random() * 500), Math.floor(Math.random() * 20)); 859 if (i % 4) 860 try { cm.markText(start, Pos(start.line + 2, 1), {collapsed: true}); } 861 catch(e) { if (!/overlapping/.test(String(e))) throw e; } 862 else 863 cm.markText(start, Pos(start.line, start.ch + 4), {"className": "foo"}); 864 } 865 }); 866 }); 867 868 testCM("hiddenLinesAutoUnfold", function(cm) { 869 var range = foldLines(cm, 1, 3, true), cleared = 0; 870 CodeMirror.on(range, "clear", function() {cleared++;}); 871 cm.setCursor(Pos(3, 0)); 872 eq(cleared, 0); 873 cm.execCommand("goCharLeft"); 874 eq(cleared, 1); 875 range = foldLines(cm, 1, 3, true); 876 CodeMirror.on(range, "clear", function() {cleared++;}); 877 eqCursorPos(cm.getCursor(), Pos(3, 0)); 878 cm.setCursor(Pos(0, 3)); 879 cm.execCommand("goCharRight"); 880 eq(cleared, 2); 881 }, {value: "abc\ndef\nghi\njkl"}); 882 883 testCM("hiddenLinesSelectAll", function(cm) { // Issue #484 884 addDoc(cm, 4, 20); 885 foldLines(cm, 0, 10); 886 foldLines(cm, 11, 20); 887 CodeMirror.commands.selectAll(cm); 888 eqCursorPos(cm.getCursor(true), Pos(10, 0)); 889 eqCursorPos(cm.getCursor(false), Pos(10, 4)); 890 }); 891 892 893 testCM("everythingFolded", function(cm) { 894 addDoc(cm, 2, 2); 895 function enterPress() { 896 cm.triggerOnKeyDown({type: "keydown", keyCode: 13, preventDefault: function(){}, stopPropagation: function(){}}); 897 } 898 var fold = foldLines(cm, 0, 2); 899 enterPress(); 900 eq(cm.getValue(), "xx\nxx"); 901 fold.clear(); 902 fold = foldLines(cm, 0, 2, true); 903 eq(fold.find(), null); 904 enterPress(); 905 eq(cm.getValue(), "\nxx\nxx"); 906 }); 907 908 testCM("structuredFold", function(cm) { 909 if (phantom) return; 910 addDoc(cm, 4, 8); 911 var range = cm.markText(Pos(1, 2), Pos(6, 2), { 912 replacedWith: document.createTextNode("Q") 913 }); 914 cm.setCursor(0, 3); 915 CodeMirror.commands.goLineDown(cm); 916 eqCharPos(cm.getCursor(), Pos(6, 2)); 917 CodeMirror.commands.goCharLeft(cm); 918 eqCharPos(cm.getCursor(), Pos(1, 2)); 919 CodeMirror.commands.delCharAfter(cm); 920 eq(cm.getValue(), "xxxx\nxxxx\nxxxx"); 921 addDoc(cm, 4, 8); 922 range = cm.markText(Pos(1, 2), Pos(6, 2), { 923 replacedWith: document.createTextNode("M"), 924 clearOnEnter: true 925 }); 926 var cleared = 0; 927 CodeMirror.on(range, "clear", function(){++cleared;}); 928 cm.setCursor(0, 3); 929 CodeMirror.commands.goLineDown(cm); 930 eqCharPos(cm.getCursor(), Pos(6, 2)); 931 CodeMirror.commands.goCharLeft(cm); 932 eqCharPos(cm.getCursor(), Pos(6, 1)); 933 eq(cleared, 1); 934 range.clear(); 935 eq(cleared, 1); 936 range = cm.markText(Pos(1, 2), Pos(6, 2), { 937 replacedWith: document.createTextNode("Q"), 938 clearOnEnter: true 939 }); 940 range.clear(); 941 cm.setCursor(1, 2); 942 CodeMirror.commands.goCharRight(cm); 943 eqCharPos(cm.getCursor(), Pos(1, 3)); 944 range = cm.markText(Pos(2, 0), Pos(4, 4), { 945 replacedWith: document.createTextNode("M") 946 }); 947 cm.setCursor(1, 0); 948 CodeMirror.commands.goLineDown(cm); 949 eqCharPos(cm.getCursor(), Pos(2, 0)); 950 }, null); 951 952 testCM("nestedFold", function(cm) { 953 addDoc(cm, 10, 3); 954 function fold(ll, cl, lr, cr) { 955 return cm.markText(Pos(ll, cl), Pos(lr, cr), {collapsed: true}); 956 } 957 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); 958 cm.setCursor(0, 1); 959 CodeMirror.commands.goCharRight(cm); 960 eqCursorPos(cm.getCursor(), Pos(2, 3)); 961 inner0.clear(); 962 CodeMirror.commands.goCharLeft(cm); 963 eqCursorPos(cm.getCursor(), Pos(0, 1)); 964 outer.clear(); 965 CodeMirror.commands.goCharRight(cm); 966 eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); 967 CodeMirror.commands.goCharRight(cm); 968 eqCursorPos(cm.getCursor(), Pos(1, 8)); 969 inner2.clear(); 970 CodeMirror.commands.goCharLeft(cm); 971 eqCursorPos(cm.getCursor(), Pos(1, 7, "after")); 972 cm.setCursor(0, 5); 973 CodeMirror.commands.goCharRight(cm); 974 eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); 975 CodeMirror.commands.goCharRight(cm); 976 eqCursorPos(cm.getCursor(), Pos(1, 3)); 977 }); 978 979 testCM("badNestedFold", function(cm) { 980 addDoc(cm, 4, 4); 981 cm.markText(Pos(0, 2), Pos(3, 2), {collapsed: true}); 982 var caught; 983 try {cm.markText(Pos(0, 1), Pos(0, 3), {collapsed: true});} 984 catch(e) {caught = e;} 985 is(caught instanceof Error, "no error"); 986 is(/overlap/i.test(caught.message), "wrong error"); 987 }); 988 989 testCM("nestedFoldOnSide", function(cm) { 990 var m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true, inclusiveRight: true}); 991 var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}); 992 cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}).clear(); 993 try { cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true, inclusiveLeft: true}); } 994 catch(e) { var caught = e; } 995 is(caught && /overlap/i.test(caught.message)); 996 var m3 = cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}); 997 var m4 = cm.markText(Pos(2, 0), Pos(2, 1), {collapse: true, inclusiveRight: true}); 998 m1.clear(); m4.clear(); 999 m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true}); 1000 cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}).clear(); 1001 try { cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true, inclusiveRight: true}); } 1002 catch(e) { var caught = e; } 1003 is(caught && /overlap/i.test(caught.message)); 1004 }, {value: "ab\ncd\ef"}); 1005 1006 testCM("editInFold", function(cm) { 1007 addDoc(cm, 4, 6); 1008 var m = cm.markText(Pos(1, 2), Pos(3, 2), {collapsed: true}); 1009 cm.replaceRange("", Pos(0, 0), Pos(1, 3)); 1010 cm.replaceRange("", Pos(2, 1), Pos(3, 3)); 1011 cm.replaceRange("a\nb\nc\nd", Pos(0, 1), Pos(1, 0)); 1012 cm.cursorCoords(Pos(0, 0)); 1013 }); 1014 1015 testCM("wrappingInlineWidget", function(cm) { 1016 cm.setSize("11em"); 1017 var w = document.createElement("span"); 1018 w.style.color = "red"; 1019 w.innerHTML = "one two three four"; 1020 cm.markText(Pos(0, 6), Pos(0, 9), {replacedWith: w}); 1021 var cur0 = cm.cursorCoords(Pos(0, 0)), cur1 = cm.cursorCoords(Pos(0, 10)); 1022 is(cur0.top < cur1.top); 1023 is(cur0.bottom < cur1.bottom); 1024 var curL = cm.cursorCoords(Pos(0, 6)), curR = cm.cursorCoords(Pos(0, 9)); 1025 eq(curL.top, cur0.top); 1026 eq(curL.bottom, cur0.bottom); 1027 eq(curR.top, cur1.top); 1028 eq(curR.bottom, cur1.bottom); 1029 cm.replaceRange("", Pos(0, 9), Pos(0)); 1030 curR = cm.cursorCoords(Pos(0, 9)); 1031 if (phantom) return; 1032 eq(curR.top, cur1.top); 1033 eq(curR.bottom, cur1.bottom); 1034 }, {value: "1 2 3 xxx 4", lineWrapping: true}); 1035 1036 testCM("showEmptyWidgetSpan", function(cm) { 1037 var marker = cm.markText(Pos(0, 2), Pos(0, 2), { 1038 clearWhenEmpty: false, 1039 replacedWith: document.createTextNode("X") 1040 }); 1041 var text = cm.display.view[0].text; 1042 eq(text.textContent || text.innerText, "abXc"); 1043 }, {value: "abc"}); 1044 1045 testCM("changedInlineWidget", function(cm) { 1046 cm.setSize("10em"); 1047 var w = document.createElement("span"); 1048 w.innerHTML = "x"; 1049 var m = cm.markText(Pos(0, 4), Pos(0, 5), {replacedWith: w}); 1050 w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed"; 1051 m.changed(); 1052 var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0]; 1053 is(hScroll.scrollWidth > hScroll.clientWidth); 1054 }, {value: "hello there"}); 1055 1056 testCM("changedBookmark", function(cm) { 1057 cm.setSize("10em"); 1058 var w = document.createElement("span"); 1059 w.innerHTML = "x"; 1060 var m = cm.setBookmark(Pos(0, 4), {widget: w}); 1061 w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed"; 1062 m.changed(); 1063 var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0]; 1064 is(hScroll.scrollWidth > hScroll.clientWidth); 1065 }, {value: "abcdefg"}); 1066 1067 testCM("inlineWidget", function(cm) { 1068 var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")}); 1069 cm.setCursor(0, 2); 1070 CodeMirror.commands.goLineDown(cm); 1071 eqCharPos(cm.getCursor(), Pos(1, 4)); 1072 cm.setCursor(0, 2); 1073 cm.replaceSelection("hi"); 1074 eqCharPos(w.find(), Pos(0, 2)); 1075 cm.setCursor(0, 1); 1076 cm.replaceSelection("ay"); 1077 eqCharPos(w.find(), Pos(0, 4)); 1078 eq(cm.getLine(0), "uayuhiuu"); 1079 }, {value: "uuuu\nuuuuuu"}); 1080 1081 testCM("wrappingAndResizing", function(cm) { 1082 cm.setSize(null, "auto"); 1083 cm.setOption("lineWrapping", true); 1084 var wrap = cm.getWrapperElement(), h0 = wrap.offsetHeight; 1085 var doc = "xxx xxx xxx xxx xxx"; 1086 cm.setValue(doc); 1087 for (var step = 10, w = cm.charCoords(Pos(0, 18), "div").right;; w += step) { 1088 cm.setSize(w); 1089 if (wrap.offsetHeight <= h0 * (opera_lt10 ? 1.2 : 1.5)) { 1090 if (step == 10) { w -= 10; step = 1; } 1091 else break; 1092 } 1093 } 1094 // Ensure that putting the cursor at the end of the maximally long 1095 // line doesn't cause wrapping to happen. 1096 cm.setCursor(Pos(0, doc.length)); 1097 eq(wrap.offsetHeight, h0); 1098 cm.replaceSelection("x"); 1099 is(wrap.offsetHeight > h0, "wrapping happens"); 1100 // Now add a max-height and, in a document consisting of 1101 // almost-wrapped lines, go over it so that a scrollbar appears. 1102 cm.setValue(doc + "\n" + doc + "\n"); 1103 cm.getScrollerElement().style.maxHeight = "100px"; 1104 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)); 1105 forEach([Pos(0, doc.length), Pos(0, doc.length - 1), 1106 Pos(0, 0), Pos(1, doc.length), Pos(1, doc.length - 1)], 1107 function(pos) { 1108 var coords = cm.charCoords(pos); 1109 eqCharPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5})); 1110 }); 1111 }, null, ie_lt8); 1112 1113 testCM("measureEndOfLine", function(cm) { 1114 if (phantom) return; 1115 cm.setSize(null, "auto"); 1116 var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; 1117 var lh = inner.offsetHeight; 1118 for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) { 1119 cm.setSize(w); 1120 if (inner.offsetHeight < 2.5 * lh) { 1121 if (step == 10) { w -= 10; step = 1; } 1122 else break; 1123 } 1124 } 1125 cm.setValue(cm.getValue() + "\n\n"); 1126 var endPos = cm.charCoords(Pos(0, 18), "local"); 1127 is(endPos.top > lh * .8, "not at top"); 1128 is(endPos.left > w - 20, "at right"); 1129 endPos = cm.charCoords(Pos(0, 18)); 1130 eqCursorPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18, "before")); 1131 1132 var wrapPos = cm.cursorCoords(Pos(0, 9, "before")); 1133 is(wrapPos.top < endPos.top, "wrapPos is actually in first line"); 1134 eqCursorPos(cm.coordsChar({left: wrapPos.left + 10, top: wrapPos.top}), Pos(0, 9, "before")); 1135 }, {mode: "text/html", value: "<!-- foo barrr -->", lineWrapping: true}, ie_lt8 || opera_lt10); 1136 1137 testCM("measureWrappedEndOfLine", function(cm) { 1138 if (phantom) return; 1139 cm.setSize(null, "auto"); 1140 var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; 1141 var lh = inner.offsetHeight; 1142 for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) { 1143 cm.setSize(w); 1144 if (inner.offsetHeight < 2.5 * lh) { 1145 if (step == 10) { w -= 10; step = 1; } 1146 else break; 1147 } 1148 } 1149 for (var i = 0; i < 3; ++i) { 1150 var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) 1151 endPos.left += w; // Add width of editor just to be sure that we are behind last character 1152 eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); 1153 endPos.left += w * 100; 1154 eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); 1155 cm.setValue("0123456789abcابجابجابجابج"); 1156 if (i == 1) { 1157 var node = document.createElement("div"); 1158 node.innerHTML = "hi"; node.style.height = "30px"; 1159 cm.addLineWidget(0, node, {above: true}); 1160 } 1161 } 1162 }, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); 1163 1164 testCM("measureEndOfLineBidi", function(cm) { 1165 eqCursorPos(cm.coordsChar({left: 5000, top: cm.charCoords(Pos(0, 0)).top}), Pos(0, 8, "after")) 1166 }, {value: "إإإإuuuuإإإإ"}) 1167 1168 testCM("measureWrappedBidiLevel2", function(cm) { 1169 cm.setSize(cm.charCoords(Pos(0, 6), "editor").right + 60) 1170 var c9 = cm.charCoords(Pos(0, 9)) 1171 eqCharPos(cm.coordsChar({left: c9.right - 1, top: c9.top + 1}), Pos(0, 9)) 1172 }, {value: "foobar إإ إإ إإ إإ 555 بببببب", lineWrapping: true}) 1173 1174 testCM("measureWrappedBeginOfLine", function(cm) { 1175 if (phantom) return; 1176 cm.setSize(null, "auto"); 1177 var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; 1178 var lh = inner.offsetHeight; 1179 for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) { 1180 cm.setSize(w); 1181 if (inner.offsetHeight < 2.5 * lh) { 1182 if (step == 10) { w -= 10; step = 1; } 1183 else break; 1184 } 1185 } 1186 var beginOfSecondLine = Pos(0, 13, "after"); 1187 for (var i = 0; i < 2; ++i) { 1188 var beginPos = cm.charCoords(Pos(0, 0)); 1189 beginPos.left -= w; 1190 eqCursorPos(cm.coordsChar(beginPos), Pos(0, 0, "after")); 1191 beginPos = cm.cursorCoords(beginOfSecondLine); 1192 beginPos.left = 0; 1193 eqCursorPos(cm.coordsChar(beginPos), beginOfSecondLine); 1194 cm.setValue("0123456789abcابجابجابجابج"); 1195 beginOfSecondLine = Pos(0, 25, "before"); 1196 } 1197 }, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}); 1198 1199 testCM("scrollVerticallyAndHorizontally", function(cm) { 1200 if (cm.getOption("inputStyle") != "textarea") return; 1201 cm.setSize(100, 100); 1202 addDoc(cm, 40, 40); 1203 cm.setCursor(39); 1204 var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-vscrollbar")[0]; 1205 is(bar.offsetHeight < wrap.offsetHeight, "vertical scrollbar limited by horizontal one"); 1206 var cursorBox = byClassName(wrap, "CodeMirror-cursor")[0].getBoundingClientRect(); 1207 var editorBox = wrap.getBoundingClientRect(); 1208 is(cursorBox.bottom < editorBox.top + cm.getScrollerElement().clientHeight, 1209 "bottom line visible"); 1210 }, {lineNumbers: true}); 1211 1212 testCM("moveVstuck", function(cm) { 1213 var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight; 1214 var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n"; 1215 cm.setValue(val); 1216 for (var w = cm.charCoords(Pos(0, 26), "div").right * 2.8;; w += 5) { 1217 cm.setSize(w); 1218 if (lines.offsetHeight <= 3.5 * h0) break; 1219 } 1220 cm.setCursor(Pos(0, val.length - 1)); 1221 cm.moveV(-1, "line"); 1222 eqCursorPos(cm.getCursor(), Pos(0, 27, "before")); 1223 is(cm.cursorCoords(null, "local").top < h0, "cursor is in first visual line"); 1224 }, {lineWrapping: true}, ie_lt8 || opera_lt10); 1225 1226 testCM("collapseOnMove", function(cm) { 1227 cm.setSelection(Pos(0, 1), Pos(2, 4)); 1228 cm.execCommand("goLineUp"); 1229 is(!cm.somethingSelected()); 1230 eqCharPos(cm.getCursor(), Pos(0, 1)); 1231 cm.setSelection(Pos(0, 1), Pos(2, 4)); 1232 cm.execCommand("goPageDown"); 1233 is(!cm.somethingSelected()); 1234 eqCharPos(cm.getCursor(), Pos(2, 4)); 1235 cm.execCommand("goLineUp"); 1236 cm.execCommand("goLineUp"); 1237 eqCharPos(cm.getCursor(), Pos(0, 4)); 1238 cm.setSelection(Pos(0, 1), Pos(2, 4)); 1239 cm.execCommand("goCharLeft"); 1240 is(!cm.somethingSelected()); 1241 eqCharPos(cm.getCursor(), Pos(0, 1)); 1242 }, {value: "aaaaa\nb\nccccc"}); 1243 1244 testCM("clickTab", function(cm) { 1245 var p0 = cm.charCoords(Pos(0, 0)); 1246 eqCharPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0)); 1247 eqCharPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), Pos(0, 1)); 1248 }, {value: "\t\n\n", lineWrapping: true, tabSize: 8}); 1249 1250 testCM("verticalScroll", function(cm) { 1251 cm.setSize(100, 200); 1252 cm.setValue("foo\nbar\nbaz\n"); 1253 var sc = cm.getScrollerElement(), baseWidth = sc.scrollWidth; 1254 cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0)); 1255 is(sc.scrollWidth > baseWidth, "scrollbar present"); 1256 cm.replaceRange("foo", Pos(0, 0), Pos(0)); 1257 if (!phantom) eq(sc.scrollWidth, baseWidth, "scrollbar gone"); 1258 cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0)); 1259 cm.replaceRange("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh", Pos(1, 0), Pos(1)); 1260 is(sc.scrollWidth > baseWidth, "present again"); 1261 var curWidth = sc.scrollWidth; 1262 cm.replaceRange("foo", Pos(0, 0), Pos(0)); 1263 is(sc.scrollWidth < curWidth, "scrollbar smaller"); 1264 is(sc.scrollWidth > baseWidth, "but still present"); 1265 }); 1266 1267 testCM("extraKeys", function(cm) { 1268 var outcome; 1269 function fakeKey(expected, code, props) { 1270 if (typeof code == "string") code = code.charCodeAt(0); 1271 var e = {type: "keydown", keyCode: code, preventDefault: function(){}, stopPropagation: function(){}}; 1272 if (props) for (var n in props) e[n] = props[n]; 1273 outcome = null; 1274 cm.triggerOnKeyDown(e); 1275 eq(outcome, expected); 1276 } 1277 CodeMirror.commands.testCommand = function() {outcome = "tc";}; 1278 CodeMirror.commands.goTestCommand = function() {outcome = "gtc";}; 1279 cm.setOption("extraKeys", {"Shift-X": function() {outcome = "sx";}, 1280 "X": function() {outcome = "x";}, 1281 "Ctrl-Alt-U": function() {outcome = "cau";}, 1282 "End": "testCommand", 1283 "Home": "goTestCommand", 1284 "Tab": false}); 1285 fakeKey(null, "U"); 1286 fakeKey("cau", "U", {ctrlKey: true, altKey: true}); 1287 fakeKey(null, "U", {shiftKey: true, ctrlKey: true, altKey: true}); 1288 fakeKey("x", "X"); 1289 fakeKey("sx", "X", {shiftKey: true}); 1290 fakeKey("tc", 35); 1291 fakeKey(null, 35, {shiftKey: true}); 1292 fakeKey("gtc", 36); 1293 fakeKey("gtc", 36, {shiftKey: true}); 1294 fakeKey(null, 9); 1295 }, null, window.opera && mac); 1296 1297 testCM("wordMovementCommands", function(cm) { 1298 cm.execCommand("goWordLeft"); 1299 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1300 cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); 1301 eqCursorPos(cm.getCursor(), Pos(0, 7, "before")); 1302 cm.execCommand("goWordLeft"); 1303 eqCursorPos(cm.getCursor(), Pos(0, 5, "after")); 1304 cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); 1305 eqCursorPos(cm.getCursor(), Pos(0, 12, "before")); 1306 cm.execCommand("goWordLeft"); 1307 eqCursorPos(cm.getCursor(), Pos(0, 9, "after")); 1308 cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); 1309 eqCursorPos(cm.getCursor(), Pos(0, 24, "before")); 1310 cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); 1311 eqCursorPos(cm.getCursor(), Pos(1, 9, "before")); 1312 cm.execCommand("goWordRight"); 1313 eqCursorPos(cm.getCursor(), Pos(1, 13, "before")); 1314 cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); 1315 eqCharPos(cm.getCursor(), Pos(2, 0)); 1316 }, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"}); 1317 1318 testCM("groupMovementCommands", function(cm) { 1319 cm.execCommand("goGroupLeft"); 1320 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1321 cm.execCommand("goGroupRight"); 1322 eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); 1323 cm.execCommand("goGroupRight"); 1324 eqCursorPos(cm.getCursor(), Pos(0, 7, "before")); 1325 cm.execCommand("goGroupRight"); 1326 eqCursorPos(cm.getCursor(), Pos(0, 10, "before")); 1327 cm.execCommand("goGroupLeft"); 1328 eqCursorPos(cm.getCursor(), Pos(0, 7, "after")); 1329 cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); 1330 eqCursorPos(cm.getCursor(), Pos(0, 15, "before")); 1331 cm.setCursor(Pos(0, 17)); 1332 cm.execCommand("goGroupLeft"); 1333 eqCursorPos(cm.getCursor(), Pos(0, 16, "after")); 1334 cm.execCommand("goGroupLeft"); 1335 eqCursorPos(cm.getCursor(), Pos(0, 14, "after")); 1336 cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); 1337 eqCursorPos(cm.getCursor(), Pos(0, 20, "before")); 1338 cm.execCommand("goGroupRight"); 1339 eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); 1340 cm.execCommand("goGroupRight"); 1341 eqCursorPos(cm.getCursor(), Pos(1, 2, "before")); 1342 cm.execCommand("goGroupRight"); 1343 eqCursorPos(cm.getCursor(), Pos(1, 5, "before")); 1344 cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft"); 1345 eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); 1346 cm.execCommand("goGroupLeft"); 1347 eqCursorPos(cm.getCursor(), Pos(0, 20, "after")); 1348 cm.execCommand("goGroupLeft"); 1349 eqCursorPos(cm.getCursor(), Pos(0, 16, "after")); 1350 }, {value: "booo ba---quux. ffff\n abc d"}); 1351 1352 testCM("groupsAndWhitespace", function(cm) { 1353 var positions = [Pos(0, 0), Pos(0, 2), Pos(0, 5), Pos(0, 9), Pos(0, 11), 1354 Pos(1, 0), Pos(1, 2), Pos(1, 5)]; 1355 for (var i = 1; i < positions.length; i++) { 1356 cm.execCommand("goGroupRight"); 1357 eqCharPos(cm.getCursor(), positions[i]); 1358 } 1359 for (var i = positions.length - 2; i >= 0; i--) { 1360 cm.execCommand("goGroupLeft"); 1361 eqCharPos(cm.getCursor(), i == 2 ? Pos(0, 6, "before") : positions[i]); 1362 } 1363 }, {value: " foo +++ \n bar"}); 1364 1365 testCM("charMovementCommands", function(cm) { 1366 cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft"); 1367 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1368 cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); 1369 eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); 1370 cm.setCursor(Pos(1, 0)); 1371 cm.execCommand("goColumnLeft"); 1372 eqCursorPos(cm.getCursor(), Pos(1, 0)); 1373 cm.execCommand("goCharLeft"); 1374 eqCursorPos(cm.getCursor(), Pos(0, 5, "before")); 1375 cm.execCommand("goColumnRight"); 1376 eqCursorPos(cm.getCursor(), Pos(0, 5, "before")); 1377 cm.execCommand("goCharRight"); 1378 eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); 1379 cm.execCommand("goLineEnd"); 1380 eqCursorPos(cm.getCursor(), Pos(1, 5, "before")); 1381 cm.execCommand("goLineStartSmart"); 1382 eqCursorPos(cm.getCursor(), Pos(1, 1, "after")); 1383 cm.execCommand("goLineStartSmart"); 1384 eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); 1385 cm.setCursor(Pos(2, 0)); 1386 cm.execCommand("goCharRight"); cm.execCommand("goColumnRight"); 1387 eqCursorPos(cm.getCursor(), Pos(2, 0)); 1388 }, {value: "line1\n ine2\n"}); 1389 1390 testCM("verticalMovementCommands", function(cm) { 1391 cm.execCommand("goLineUp"); 1392 eqCharPos(cm.getCursor(), Pos(0, 0)); 1393 cm.execCommand("goLineDown"); 1394 if (!phantom) // This fails in PhantomJS, though not in a real Webkit 1395 eqCharPos(cm.getCursor(), Pos(1, 0)); 1396 cm.setCursor(Pos(1, 12)); 1397 cm.execCommand("goLineDown"); 1398 eqCharPos(cm.getCursor(), Pos(2, 5)); 1399 cm.execCommand("goLineDown"); 1400 eqCharPos(cm.getCursor(), Pos(3, 0)); 1401 cm.execCommand("goLineUp"); 1402 eqCharPos(cm.getCursor(), Pos(2, 5)); 1403 cm.execCommand("goLineUp"); 1404 eqCharPos(cm.getCursor(), Pos(1, 12)); 1405 cm.execCommand("goPageDown"); 1406 eqCharPos(cm.getCursor(), Pos(5, 0)); 1407 cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); 1408 eqCharPos(cm.getCursor(), Pos(5, 0)); 1409 cm.execCommand("goPageUp"); 1410 eqCharPos(cm.getCursor(), Pos(0, 0)); 1411 }, {value: "line1\nlong long line2\nline3\n\nline5\n"}); 1412 1413 testCM("verticalMovementCommandsWrapping", function(cm) { 1414 cm.setSize(120); 1415 cm.setCursor(Pos(0, 5)); 1416 cm.execCommand("goLineDown"); 1417 eq(cm.getCursor().line, 0); 1418 is(cm.getCursor().ch > 5, "moved beyond wrap"); 1419 for (var i = 0; ; ++i) { 1420 is(i < 20, "no endless loop"); 1421 cm.execCommand("goLineDown"); 1422 var cur = cm.getCursor(); 1423 if (cur.line == 1) eq(cur.ch, 5); 1424 if (cur.line == 2) { eq(cur.ch, 1); break; } 1425 } 1426 }, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk", 1427 lineWrapping: true}); 1428 1429 testCM("verticalMovementCommandsSingleLine", function(cm) { 1430 cm.display.wrapper.style.height = "auto"; 1431 cm.refresh(); 1432 cm.execCommand("goLineUp"); 1433 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1434 cm.execCommand("goLineDown"); 1435 eqCursorPos(cm.getCursor(), Pos(0, 11)); 1436 cm.setCursor(Pos(0, 5)); 1437 cm.execCommand("goLineDown"); 1438 eqCursorPos(cm.getCursor(), Pos(0, 11)); 1439 cm.execCommand("goLineDown"); 1440 eqCursorPos(cm.getCursor(), Pos(0, 11)); 1441 cm.execCommand("goLineUp"); 1442 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1443 cm.execCommand("goLineUp"); 1444 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1445 cm.execCommand("goPageDown"); 1446 eqCursorPos(cm.getCursor(), Pos(0, 11)); 1447 cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); 1448 eqCursorPos(cm.getCursor(), Pos(0, 11)); 1449 cm.execCommand("goPageUp"); 1450 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1451 cm.setCursor(Pos(0, 5)); 1452 cm.execCommand("goPageUp"); 1453 eqCursorPos(cm.getCursor(), Pos(0, 0)); 1454 cm.setCursor(Pos(0, 5)); 1455 cm.execCommand("goPageDown"); 1456 eqCursorPos(cm.getCursor(), Pos(0, 11)); 1457 }, {value: "single line"}); 1458 1459 1460 testCM("rtlMovement", function(cm) { 1461 if (cm.getOption("inputStyle") != "textarea") return; 1462 forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", 1463 "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق", 1464 "<img src=\"/בדיקה3.jpg\">", "يتم السحب في 05 فبراير 2014"], function(line) { 1465 cm.setValue(line + "\n"); cm.execCommand("goLineStart"); 1466 var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0]; 1467 var cursor = cursors.firstChild; 1468 var prevX = cursor.offsetLeft, prevY = cursor.offsetTop; 1469 for (var i = 0; i <= line.length; ++i) { 1470 cm.execCommand("goCharRight"); 1471 cursor = cursors.firstChild; 1472 if (i == line.length) is(cursor.offsetTop > prevY, "next line"); 1473 else is(cursor.offsetLeft > prevX, "moved right"); 1474 prevX = cursor.offsetLeft; prevY = cursor.offsetTop; 1475 } 1476 cm.setCursor(0, 0); cm.execCommand("goLineEnd"); 1477 prevX = cursors.firstChild.offsetLeft; 1478 for (var i = 0; i < line.length; ++i) { 1479 cm.execCommand("goCharLeft"); 1480 cursor = cursors.firstChild; 1481 is(cursor.offsetLeft < prevX, "moved left"); 1482 prevX = cursor.offsetLeft; 1483 } 1484 }); 1485 }, null, ie_lt9); 1486 1487 // Verify that updating a line clears its bidi ordering 1488 testCM("bidiUpdate", function(cm) { 1489 cm.setCursor(Pos(0, 2, "before")); 1490 cm.replaceSelection("خحج", "start"); 1491 cm.execCommand("goCharRight"); 1492 eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); 1493 }, {value: "abcd\n"}); 1494 1495 testCM("movebyTextUnit", function(cm) { 1496 cm.setValue("בְּרֵאשִ\nééé́\n"); 1497 cm.execCommand("goLineStart"); 1498 for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight"); 1499 eqCursorPos(cm.getCursor(), Pos(0, 0, "after")); 1500 cm.execCommand("goCharRight"); 1501 eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); 1502 cm.execCommand("goCharRight"); 1503 cm.execCommand("goCharRight"); 1504 eqCursorPos(cm.getCursor(), Pos(1, 4, "before")); 1505 cm.execCommand("goCharRight"); 1506 eqCursorPos(cm.getCursor(), Pos(1, 7, "before")); 1507 }); 1508 1509 testCM("lineChangeEvents", function(cm) { 1510 addDoc(cm, 3, 5); 1511 var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"]; 1512 for (var i = 0; i < 5; ++i) { 1513 CodeMirror.on(cm.getLineHandle(i), "delete", function(i) { 1514 return function() {log.push("del " + i);}; 1515 }(i)); 1516 CodeMirror.on(cm.getLineHandle(i), "change", function(i) { 1517 return function() {log.push("ch " + i);}; 1518 }(i)); 1519 } 1520 cm.replaceRange("x", Pos(0, 1)); 1521 cm.replaceRange("xy", Pos(1, 1), Pos(2)); 1522 cm.replaceRange("foo\nbar", Pos(0, 1)); 1523 cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount())); 1524 eq(log.length, want.length, "same length"); 1525 for (var i = 0; i < log.length; ++i) 1526 eq(log[i], want[i]); 1527 }); 1528 1529 testCM("scrollEntirelyToRight", function(cm) { 1530 if (phantom || cm.getOption("inputStyle") != "textarea") return; 1531 addDoc(cm, 500, 2); 1532 cm.setCursor(Pos(0, 500)); 1533 var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0]; 1534 is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left); 1535 }); 1536 1537 testCM("lineWidgets", function(cm) { 1538 addDoc(cm, 500, 3); 1539 var last = cm.charCoords(Pos(2, 0)); 1540 var node = document.createElement("div"); 1541 node.innerHTML = "hi"; 1542 var widget = cm.addLineWidget(1, node); 1543 is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space"); 1544 cm.setCursor(Pos(1, 1)); 1545 cm.execCommand("goLineDown"); 1546 eqCharPos(cm.getCursor(), Pos(2, 1)); 1547 cm.execCommand("goLineUp"); 1548 eqCharPos(cm.getCursor(), Pos(1, 1)); 1549 }); 1550 1551 testCM("lineWidgetFocus", function(cm) { 1552 var place = document.getElementById("testground"); 1553 place.className = "offscreen"; 1554 try { 1555 addDoc(cm, 500, 10); 1556 var node = document.createElement("input"); 1557 var widget = cm.addLineWidget(1, node); 1558 node.focus(); 1559 eq(document.activeElement, node); 1560 cm.replaceRange("new stuff", Pos(1, 0)); 1561 eq(document.activeElement, node); 1562 } finally { 1563 place.className = ""; 1564 } 1565 }); 1566 1567 testCM("lineWidgetCautiousRedraw", function(cm) { 1568 var node = document.createElement("div"); 1569 node.innerHTML = "hahah"; 1570 var w = cm.addLineWidget(0, node); 1571 var redrawn = false; 1572 w.on("redraw", function() { redrawn = true; }); 1573 cm.replaceSelection("0"); 1574 is(!redrawn); 1575 }, {value: "123\n456"}); 1576 1577 1578 var knownScrollbarWidth; 1579 function scrollbarWidth(measure) { 1580 if (knownScrollbarWidth != null) return knownScrollbarWidth; 1581 var div = document.createElement('div'); 1582 div.style.cssText = "width: 50px; height: 50px; overflow-x: scroll"; 1583 document.body.appendChild(div); 1584 knownScrollbarWidth = div.offsetHeight - div.clientHeight; 1585 document.body.removeChild(div); 1586 return knownScrollbarWidth || 0; 1587 } 1588 1589 testCM("lineWidgetChanged", function(cm) { 1590 addDoc(cm, 2, 300); 1591 var halfScrollbarWidth = scrollbarWidth(cm.display.measure)/2; 1592 cm.setOption('lineNumbers', true); 1593 cm.setSize(600, cm.defaultTextHeight() * 50); 1594 cm.scrollTo(null, cm.heightAtLine(125, "local")); 1595 1596 var expectedWidgetHeight = 60; 1597 var expectedLinesInWidget = 3; 1598 function w() { 1599 var node = document.createElement("div"); 1600 // we use these children with just under half width of the line to check measurements are made with correct width 1601 // when placed in the measure div. 1602 // If the widget is measured at a width much narrower than it is displayed at, the underHalf children will span two lines and break the test. 1603 // If the widget is measured at a width much wider than it is displayed at, the overHalf children will combine and break the test. 1604 // Note that this test only checks widgets where coverGutter is true, because these require extra styling to get the width right. 1605 // It may also be worthwhile to check this for non-coverGutter widgets. 1606 // Visually: 1607 // Good: 1608 // | ------------- display width ------------- | 1609 // | ------- widget-width when measured ------ | 1610 // | | -- under-half -- | | -- under-half -- | | 1611 // | | --- over-half --- | | 1612 // | | --- over-half --- | | 1613 // Height: measured as 3 lines, same as it will be when actually displayed 1614 1615 // Bad (too narrow): 1616 // | ------------- display width ------------- | 1617 // | ------ widget-width when measured ----- | < -- uh oh 1618 // | | -- under-half -- | | 1619 // | | -- under-half -- | | < -- when measured, shoved to next line 1620 // | | --- over-half --- | | 1621 // | | --- over-half --- | | 1622 // Height: measured as 4 lines, more than expected . Will be displayed as 3 lines! 1623 1624 // Bad (too wide): 1625 // | ------------- display width ------------- | 1626 // | -------- widget-width when measured ------- | < -- uh oh 1627 // | | -- under-half -- | | -- under-half -- | | 1628 // | | --- over-half --- | | --- over-half --- | | < -- when measured, combined on one line 1629 // Height: measured as 2 lines, less than expected. Will be displayed as 3 lines! 1630 1631 var barelyUnderHalfWidthHtml = '<div style="display: inline-block; height: 1px; width: '+(285 - halfScrollbarWidth)+'px;"></div>'; 1632 var barelyOverHalfWidthHtml = '<div style="display: inline-block; height: 1px; width: '+(305 - halfScrollbarWidth)+'px;"></div>'; 1633 node.innerHTML = new Array(3).join(barelyUnderHalfWidthHtml) + new Array(3).join(barelyOverHalfWidthHtml); 1634 node.style.cssText = "background: yellow;font-size:0;line-height: " + (expectedWidgetHeight/expectedLinesInWidget) + "px;"; 1635 return node; 1636 } 1637 var info0 = cm.getScrollInfo(); 1638 var w0 = cm.addLineWidget(0, w(), { coverGutter: true }); 1639 var w150 = cm.addLineWidget(150, w(), { coverGutter: true }); 1640 var w300 = cm.addLineWidget(300, w(), { coverGutter: true }); 1641 var info1 = cm.getScrollInfo(); 1642 eq(info0.height + (3 * expectedWidgetHeight), info1.height); 1643 eq(info0.top + expectedWidgetHeight, info1.top); 1644 expectedWidgetHeight = 12; 1645 w0.node.style.lineHeight = w150.node.style.lineHeight = w300.node.style.lineHeight = (expectedWidgetHeight/expectedLinesInWidget) + "px"; 1646 w0.changed(); w150.changed(); w300.changed(); 1647 var info2 = cm.getScrollInfo(); 1648 eq(info0.height + (3 * expectedWidgetHeight), info2.height); 1649 eq(info0.top + expectedWidgetHeight, info2.top); 1650 }); 1651 1652 testCM("getLineNumber", function(cm) { 1653 addDoc(cm, 2, 20); 1654 var h1 = cm.getLineHandle(1); 1655 eq(cm.getLineNumber(h1), 1); 1656 cm.replaceRange("hi\nbye\n", Pos(0, 0)); 1657 eq(cm.getLineNumber(h1), 3); 1658 cm.setValue(""); 1659 eq(cm.getLineNumber(h1), null); 1660 }); 1661 1662 testCM("jumpTheGap", function(cm) { 1663 if (phantom) return; 1664 var longLine = "abcdef ghiklmnop qrstuvw xyz "; 1665 longLine += longLine; longLine += longLine; longLine += longLine; 1666 cm.replaceRange(longLine, Pos(2, 0), Pos(2)); 1667 cm.setSize("200px", null); 1668 cm.getWrapperElement().style.lineHeight = 2; 1669 cm.refresh(); 1670 cm.setCursor(Pos(0, 1)); 1671 cm.execCommand("goLineDown"); 1672 eqCharPos(cm.getCursor(), Pos(1, 1)); 1673 cm.execCommand("goLineDown"); 1674 eqCharPos(cm.getCursor(), Pos(2, 1)); 1675 cm.execCommand("goLineDown"); 1676 eq(cm.getCursor().line, 2); 1677 is(cm.getCursor().ch > 1); 1678 cm.execCommand("goLineUp"); 1679 eqCharPos(cm.getCursor(), Pos(2, 1)); 1680 cm.execCommand("goLineUp"); 1681 eqCharPos(cm.getCursor(), Pos(1, 1)); 1682 var node = document.createElement("div"); 1683 node.innerHTML = "hi"; node.style.height = "30px"; 1684 cm.addLineWidget(0, node); 1685 cm.addLineWidget(1, node.cloneNode(true), {above: true}); 1686 cm.setCursor(Pos(0, 2)); 1687 cm.execCommand("goLineDown"); 1688 eqCharPos(cm.getCursor(), Pos(1, 2)); 1689 cm.execCommand("goLineUp"); 1690 eqCharPos(cm.getCursor(), Pos(0, 2)); 1691 }, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"}); 1692 1693 testCM("addLineClass", function(cm) { 1694 function cls(line, text, bg, wrap, gutter) { 1695 var i = cm.lineInfo(line); 1696 eq(i.textClass, text); 1697 eq(i.bgClass, bg); 1698 eq(i.wrapClass, wrap); 1699 if (typeof i.handle.gutterClass !== 'undefined') { 1700 eq(i.handle.gutterClass, gutter); 1701 } 1702 } 1703 cm.addLineClass(0, "text", "foo"); 1704 cm.addLineClass(0, "text", "bar"); 1705 cm.addLineClass(1, "background", "baz"); 1706 cm.addLineClass(1, "wrap", "foo"); 1707 cm.addLineClass(1, "gutter", "gutter-class"); 1708 cls(0, "foo bar", null, null, null); 1709 cls(1, null, "baz", "foo", "gutter-class"); 1710 var lines = cm.display.lineDiv; 1711 eq(byClassName(lines, "foo").length, 2); 1712 eq(byClassName(lines, "bar").length, 1); 1713 eq(byClassName(lines, "baz").length, 1); 1714 eq(byClassName(lines, "gutter-class").length, 2); // Gutter classes are reflected in 2 nodes 1715 cm.removeLineClass(0, "text", "foo"); 1716 cls(0, "bar", null, null, null); 1717 cm.removeLineClass(0, "text", "foo"); 1718 cls(0, "bar", null, null, null); 1719 cm.removeLineClass(0, "text", "bar"); 1720 cls(0, null, null, null); 1721 1722 cm.addLineClass(1, "wrap", "quux"); 1723 cls(1, null, "baz", "foo quux", "gutter-class"); 1724 cm.removeLineClass(1, "wrap"); 1725 cls(1, null, "baz", null, "gutter-class"); 1726 cm.removeLineClass(1, "gutter", "gutter-class"); 1727 eq(byClassName(lines, "gutter-class").length, 0); 1728 cls(1, null, "baz", null, null); 1729 1730 cm.addLineClass(1, "gutter", "gutter-class"); 1731 cls(1, null, "baz", null, "gutter-class"); 1732 cm.removeLineClass(1, "gutter", "gutter-class"); 1733 cls(1, null, "baz", null, null); 1734 1735 }, {value: "hohoho\n", lineNumbers: true}); 1736 1737 testCM("atomicMarker", function(cm) { 1738 addDoc(cm, 10, 10); 1739 function atom(ll, cl, lr, cr, li, ri) { 1740 return cm.markText(Pos(ll, cl), Pos(lr, cr), 1741 {atomic: true, inclusiveLeft: li, inclusiveRight: ri}); 1742 } 1743 var m = atom(0, 1, 0, 5); 1744 cm.setCursor(Pos(0, 1)); 1745 cm.execCommand("goCharRight"); 1746 eqCursorPos(cm.getCursor(), Pos(0, 5)); 1747 cm.execCommand("goCharLeft"); 1748 eqCursorPos(cm.getCursor(), Pos(0, 1)); 1749 m.clear(); 1750 m = atom(0, 0, 0, 5, true); 1751 eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); 1752 cm.execCommand("goCharLeft"); 1753 eqCursorPos(cm.getCursor(), Pos(0, 5)); 1754 m.clear(); 1755 m = atom(8, 4, 9, 10, false, true); 1756 cm.setCursor(Pos(9, 8)); 1757 eqCursorPos(cm.getCursor(), Pos(8, 4), "set"); 1758 cm.execCommand("goCharRight"); 1759 eqCursorPos(cm.getCursor(), Pos(8, 4), "char right"); 1760 cm.execCommand("goLineDown"); 1761 eqCursorPos(cm.getCursor(), Pos(8, 4), "line down"); 1762 cm.execCommand("goCharLeft"); 1763 eqCursorPos(cm.getCursor(), Pos(8, 3, "after")); 1764 m.clear(); 1765 m = atom(1, 1, 3, 8); 1766 cm.setCursor(Pos(0, 0)); 1767 cm.setCursor(Pos(2, 0)); 1768 eqCursorPos(cm.getCursor(), Pos(3, 8)); 1769 cm.execCommand("goCharLeft"); 1770 eqCursorPos(cm.getCursor(), Pos(1, 1)); 1771 cm.execCommand("goCharRight"); 1772 eqCursorPos(cm.getCursor(), Pos(3, 8)); 1773 cm.execCommand("goLineUp"); 1774 eqCursorPos(cm.getCursor(), Pos(1, 1)); 1775 cm.execCommand("goLineDown"); 1776 eqCursorPos(cm.getCursor(), Pos(3, 8)); 1777 cm.execCommand("delCharBefore"); 1778 eq(cm.getValue().length, 80, "del chunk"); 1779 m = atom(3, 0, 5, 5); 1780 cm.setCursor(Pos(3, 0)); 1781 cm.execCommand("delWordAfter"); 1782 eq(cm.getValue().length, 53, "del chunk"); 1783 }); 1784 1785 testCM("selectionBias", function(cm) { 1786 cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true}); 1787 cm.setCursor(Pos(0, 2)); 1788 eqCursorPos(cm.getCursor(), Pos(0, 1)); 1789 cm.setCursor(Pos(0, 2)); 1790 eqCursorPos(cm.getCursor(), Pos(0, 3)); 1791 cm.setCursor(Pos(0, 2)); 1792 eqCursorPos(cm.getCursor(), Pos(0, 1)); 1793 cm.setCursor(Pos(0, 2), null, {bias: -1}); 1794 eqCursorPos(cm.getCursor(), Pos(0, 1)); 1795 cm.setCursor(Pos(0, 4)); 1796 cm.setCursor(Pos(0, 2), null, {bias: 1}); 1797 eqCursorPos(cm.getCursor(), Pos(0, 3)); 1798 }, {value: "12345"}); 1799 1800 testCM("selectionHomeEnd", function(cm) { 1801 cm.markText(Pos(1, 0), Pos(1, 1), {atomic: true, inclusiveLeft: true}); 1802 cm.markText(Pos(1, 3), Pos(1, 4), {atomic: true, inclusiveRight: true}); 1803 cm.setCursor(Pos(1, 2)); 1804 cm.execCommand("goLineStart"); 1805 eqCursorPos(cm.getCursor(), Pos(1, 1)); 1806 cm.execCommand("goLineEnd"); 1807 eqCursorPos(cm.getCursor(), Pos(1, 3)); 1808 }, {value: "ab\ncdef\ngh"}); 1809 1810 testCM("readOnlyMarker", function(cm) { 1811 function mark(ll, cl, lr, cr, at) { 1812 return cm.markText(Pos(ll, cl), Pos(lr, cr), 1813 {readOnly: true, atomic: at}); 1814 } 1815 var m = mark(0, 1, 0, 4); 1816 cm.setCursor(Pos(0, 2)); 1817 cm.replaceSelection("hi", "end"); 1818 eqCursorPos(cm.getCursor(), Pos(0, 2)); 1819 eq(cm.getLine(0), "abcde"); 1820 cm.execCommand("selectAll"); 1821 cm.replaceSelection("oops", "around"); 1822 eq(cm.getValue(), "oopsbcd"); 1823 cm.undo(); 1824 eqCursorPos(m.find().from, Pos(0, 1)); 1825 eqCursorPos(m.find().to, Pos(0, 4)); 1826 m.clear(); 1827 cm.setCursor(Pos(0, 2)); 1828 cm.replaceSelection("hi", "around"); 1829 eq(cm.getLine(0), "abhicde"); 1830 eqCursorPos(cm.getCursor(), Pos(0, 4)); 1831 m = mark(0, 2, 2, 2, true); 1832 cm.setSelection(Pos(1, 1), Pos(2, 4)); 1833 cm.replaceSelection("t", "end"); 1834 eqCursorPos(cm.getCursor(), Pos(2, 3)); 1835 eq(cm.getLine(2), "klto"); 1836 cm.execCommand("goCharLeft"); 1837 cm.execCommand("goCharLeft"); 1838 eqCursorPos(cm.getCursor(), Pos(0, 2)); 1839 cm.setSelection(Pos(0, 1), Pos(0, 3)); 1840 cm.replaceSelection("xx", "around"); 1841 eqCursorPos(cm.getCursor(), Pos(0, 3)); 1842 eq(cm.getLine(0), "axxhicde"); 1843 }, {value: "abcde\nfghij\nklmno\n"}); 1844 1845 testCM("dirtyBit", function(cm) { 1846 eq(cm.isClean(), true); 1847 cm.replaceSelection("boo", null, "test"); 1848 eq(cm.isClean(), false); 1849 cm.undo(); 1850 eq(cm.isClean(), true); 1851 cm.replaceSelection("boo", null, "test"); 1852 cm.replaceSelection("baz", null, "test"); 1853 cm.undo(); 1854 eq(cm.isClean(), false); 1855 cm.markClean(); 1856 eq(cm.isClean(), true); 1857 cm.undo(); 1858 eq(cm.isClean(), false); 1859 cm.redo(); 1860 eq(cm.isClean(), true); 1861 }); 1862 1863 testCM("changeGeneration", function(cm) { 1864 cm.replaceSelection("x"); 1865 var softGen = cm.changeGeneration(); 1866 cm.replaceSelection("x"); 1867 cm.undo(); 1868 eq(cm.getValue(), ""); 1869 is(!cm.isClean(softGen)); 1870 cm.replaceSelection("x"); 1871 var hardGen = cm.changeGeneration(true); 1872 cm.replaceSelection("x"); 1873 cm.undo(); 1874 eq(cm.getValue(), "x"); 1875 is(cm.isClean(hardGen)); 1876 }); 1877 1878 testCM("addKeyMap", function(cm) { 1879 function sendKey(code) { 1880 cm.triggerOnKeyDown({type: "keydown", keyCode: code, 1881 preventDefault: function(){}, stopPropagation: function(){}}); 1882 } 1883 1884 sendKey(39); 1885 eqCursorPos(cm.getCursor(), Pos(0, 1, "before")); 1886 var test = 0; 1887 var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }} 1888 cm.addKeyMap(map1); 1889 sendKey(39); 1890 eqCursorPos(cm.getCursor(), Pos(0, 1, "before")); 1891 eq(test, 1); 1892 cm.addKeyMap(map2, true); 1893 sendKey(39); 1894 eq(test, 2); 1895 cm.removeKeyMap(map1); 1896 sendKey(39); 1897 eq(test, 12); 1898 cm.removeKeyMap(map2); 1899 sendKey(39); 1900 eq(test, 12); 1901 eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); 1902 cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"}); 1903 sendKey(39); 1904 eq(test, 55); 1905 cm.removeKeyMap("mymap"); 1906 sendKey(39); 1907 eqCursorPos(cm.getCursor(), Pos(0, 3, "before")); 1908 }, {value: "abc"}); 1909 1910 function mouseDown(cm, button, pos, mods) { 1911 var coords = cm.charCoords(pos, "window") 1912 var event = {type: "mousedown", 1913 preventDefault: Math.min, 1914 which: button, 1915 target: cm.display.lineDiv, 1916 clientX: coords.left, clientY: coords.top} 1917 if (mods) for (var prop in mods) event[prop] = mods[prop] 1918 cm.triggerOnMouseDown(event) 1919 } 1920 1921 testCM("mouseBinding", function(cm) { 1922 var fired = [] 1923 cm.addKeyMap({ 1924 "Shift-LeftClick": function(_cm, pos) { 1925 eqCharPos(pos, Pos(1, 2)) 1926 fired.push("a") 1927 }, 1928 "Shift-LeftDoubleClick": function() { fired.push("b") }, 1929 "Shift-LeftTripleClick": function() { fired.push("c") } 1930 }) 1931 1932 function send(button, mods) { mouseDown(cm, button, Pos(1, 2), mods) } 1933 send(1, {shiftKey: true}) 1934 send(1, {shiftKey: true}) 1935 send(1, {shiftKey: true}) 1936 send(1, {}) 1937 send(2, {ctrlKey: true}) 1938 send(2, {ctrlKey: true}) 1939 eq(fired.join(" "), "a b c") 1940 }, {value: "foo\nbar\nbaz"}) 1941 1942 testCM("configureMouse", function(cm) { 1943 cm.setOption("configureMouse", function() { return {unit: "word"} }) 1944 mouseDown(cm, 1, Pos(0, 5)) 1945 eqCharPos(cm.getCursor("from"), Pos(0, 4)) 1946 eqCharPos(cm.getCursor("to"), Pos(0, 7)) 1947 cm.setOption("configureMouse", function() { return {extend: true} }) 1948 mouseDown(cm, 1, Pos(0, 0)) 1949 eqCharPos(cm.getCursor("from"), Pos(0, 0)) 1950 eqCharPos(cm.getCursor("to"), Pos(0, 4)) 1951 }, {value: "foo bar baz"}) 1952 1953 testCM("findPosH", function(cm) { 1954 forEach([{from: Pos(0, 0), to: Pos(0, 1, "before"), by: 1}, 1955 {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true}, 1956 {from: Pos(0, 0), to: Pos(0, 4, "before"), by: 1, unit: "word"}, 1957 {from: Pos(0, 0), to: Pos(0, 8, "before"), by: 2, unit: "word"}, 1958 {from: Pos(0, 0), to: Pos(2, 0, "after"), by: 20, unit: "word", hitSide: true}, 1959 {from: Pos(0, 7), to: Pos(0, 5, "after"), by: -1, unit: "word"}, 1960 {from: Pos(0, 4), to: Pos(0, 8, "before"), by: 1, unit: "word"}, 1961 {from: Pos(1, 0), to: Pos(1, 18, "before"), by: 3, unit: "word"}, 1962 {from: Pos(1, 22), to: Pos(1, 5, "after"), by: -3, unit: "word"}, 1963 {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5}, 1964 {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5, unit: "column"}, 1965 {from: Pos(1, 15), to: Pos(1, 0, "after"), by: -50, unit: "column", hitSide: true}, 1966 {from: Pos(1, 15), to: Pos(1, 24, "before"), by: 50, unit: "column", hitSide: true}, 1967 {from: Pos(1, 15), to: Pos(2, 0, "after"), by: 50, hitSide: true}], function(t) { 1968 var r = cm.findPosH(t.from, t.by, t.unit || "char"); 1969 eqCursorPos(r, t.to); 1970 eq(!!r.hitSide, !!t.hitSide); 1971 }); 1972 }, {value: "line one\nline two.something.other\n"}); 1973 1974 testCM("beforeChange", function(cm) { 1975 cm.on("beforeChange", function(cm, change) { 1976 var text = []; 1977 for (var i = 0; i < change.text.length; ++i) 1978 text.push(change.text[i].replace(/\s/g, "_")); 1979 change.update(null, null, text); 1980 }); 1981 cm.setValue("hello, i am a\nnew document\n"); 1982 eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); 1983 CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) { 1984 if (change.from.line == 0) change.cancel(); 1985 }); 1986 cm.setValue("oops"); // Canceled 1987 eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); 1988 cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0)); 1989 eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey"); 1990 }, {value: "abcdefghijk"}); 1991 1992 testCM("beforeChangeUndo", function(cm) { 1993 cm.replaceRange("hi", Pos(0, 0), Pos(0)); 1994 cm.replaceRange("bye", Pos(0, 0), Pos(0)); 1995 eq(cm.historySize().undo, 2); 1996 cm.on("beforeChange", function(cm, change) { 1997 is(!change.update); 1998 change.cancel(); 1999 }); 2000 cm.undo(); 2001 eq(cm.historySize().undo, 0); 2002 eq(cm.getValue(), "bye\ntwo"); 2003 }, {value: "one\ntwo"}); 2004 2005 testCM("beforeSelectionChange", function(cm) { 2006 function notAtEnd(cm, pos) { 2007 var len = cm.getLine(pos.line).length; 2008 if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1); 2009 return pos; 2010 } 2011 cm.on("beforeSelectionChange", function(cm, obj) { 2012 obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor), 2013 head: notAtEnd(cm, obj.ranges[0].head)}]); 2014 }); 2015 2016 addDoc(cm, 10, 10); 2017 cm.execCommand("goLineEnd"); 2018 eqCursorPos(cm.getCursor(), Pos(0, 9)); 2019 cm.execCommand("selectAll"); 2020 eqCursorPos(cm.getCursor("start"), Pos(0, 0)); 2021 eqCursorPos(cm.getCursor("end"), Pos(9, 9)); 2022 }); 2023 2024 testCM("change_removedText", function(cm) { 2025 cm.setValue("abc\ndef"); 2026 2027 var removedText = []; 2028 cm.on("change", function(cm, change) { 2029 removedText.push(change.removed); 2030 }); 2031 2032 cm.operation(function() { 2033 cm.replaceRange("xyz", Pos(0, 0), Pos(1,1)); 2034 cm.replaceRange("123", Pos(0,0)); 2035 }); 2036 2037 eq(removedText.length, 2); 2038 eq(removedText[0].join("\n"), "abc\nd"); 2039 eq(removedText[1].join("\n"), ""); 2040 2041 var removedText = []; 2042 cm.undo(); 2043 eq(removedText.length, 2); 2044 eq(removedText[0].join("\n"), "123"); 2045 eq(removedText[1].join("\n"), "xyz"); 2046 2047 var removedText = []; 2048 cm.redo(); 2049 eq(removedText.length, 2); 2050 eq(removedText[0].join("\n"), "abc\nd"); 2051 eq(removedText[1].join("\n"), ""); 2052 }); 2053 2054 testCM("lineStyleFromMode", function(cm) { 2055 CodeMirror.defineMode("test_mode", function() { 2056 return {token: function(stream) { 2057 if (stream.match(/^\[[^\]]*\]/)) return " line-brackets "; 2058 if (stream.match(/^\([^\)]*\)/)) return " line-background-parens "; 2059 if (stream.match(/^<[^>]*>/)) return " span line-line line-background-bg "; 2060 stream.match(/^\s+|^\S+/); 2061 }}; 2062 }); 2063 cm.setOption("mode", "test_mode"); 2064 var bracketElts = byClassName(cm.getWrapperElement(), "brackets"); 2065 eq(bracketElts.length, 1, "brackets count"); 2066 eq(bracketElts[0].nodeName, "PRE"); 2067 is(!/brackets.*brackets/.test(bracketElts[0].className)); 2068 var parenElts = byClassName(cm.getWrapperElement(), "parens"); 2069 eq(parenElts.length, 1, "parens count"); 2070 eq(parenElts[0].nodeName, "DIV"); 2071 is(!/parens.*parens/.test(parenElts[0].className)); 2072 eq(parenElts[0].parentElement.nodeName, "DIV"); 2073 2074 is(byClassName(cm.getWrapperElement(), "bg").length > 0); 2075 is(byClassName(cm.getWrapperElement(), "line").length > 0); 2076 var spanElts = byClassName(cm.getWrapperElement(), "cm-span"); 2077 eq(spanElts.length, 2); 2078 is(/^\s*cm-span\s*$/.test(spanElts[0].className)); 2079 }, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: <tag> <tag>"}); 2080 2081 testCM("lineStyleFromBlankLine", function(cm) { 2082 CodeMirror.defineMode("lineStyleFromBlankLine_mode", function() { 2083 return {token: function(stream) { stream.skipToEnd(); return "comment"; }, 2084 blankLine: function() { return "line-blank"; }}; 2085 }); 2086 cm.setOption("mode", "lineStyleFromBlankLine_mode"); 2087 var blankElts = byClassName(cm.getWrapperElement(), "blank"); 2088 eq(blankElts.length, 1); 2089 eq(blankElts[0].nodeName, "PRE"); 2090 cm.replaceRange("x", Pos(1, 0)); 2091 blankElts = byClassName(cm.getWrapperElement(), "blank"); 2092 eq(blankElts.length, 0); 2093 }, {value: "foo\n\nbar"}); 2094 2095 CodeMirror.registerHelper("xxx", "a", "A"); 2096 CodeMirror.registerHelper("xxx", "b", "B"); 2097 CodeMirror.defineMode("yyy", function() { 2098 return { 2099 token: function(stream) { stream.skipToEnd(); }, 2100 xxx: ["a", "b", "q"] 2101 }; 2102 }); 2103 CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C"); 2104 2105 testCM("helpers", function(cm) { 2106 cm.setOption("mode", "yyy"); 2107 eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B"); 2108 cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}}); 2109 eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C"); 2110 cm.setOption("mode", "javascript"); 2111 eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), ""); 2112 }); 2113 2114 testCM("selectionHistory", function(cm) { 2115 for (var i = 0; i < 3; i++) { 2116 cm.setExtending(true); 2117 cm.execCommand("goCharRight"); 2118 cm.setExtending(false); 2119 cm.execCommand("goCharRight"); 2120 cm.execCommand("goCharRight"); 2121 } 2122 cm.execCommand("undoSelection"); 2123 eq(cm.getSelection(), "c"); 2124 cm.execCommand("undoSelection"); 2125 eq(cm.getSelection(), ""); 2126 eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); 2127 cm.execCommand("undoSelection"); 2128 eq(cm.getSelection(), "b"); 2129 cm.execCommand("redoSelection"); 2130 eq(cm.getSelection(), ""); 2131 eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); 2132 cm.execCommand("redoSelection"); 2133 eq(cm.getSelection(), "c"); 2134 cm.execCommand("redoSelection"); 2135 eq(cm.getSelection(), ""); 2136 eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); 2137 }, {value: "a b c d"}); 2138 2139 testCM("selectionChangeReducesRedo", function(cm) { 2140 cm.replaceSelection("X"); 2141 cm.execCommand("goCharRight"); 2142 cm.undoSelection(); 2143 cm.execCommand("selectAll"); 2144 cm.undoSelection(); 2145 eq(cm.getValue(), "Xabc"); 2146 eqCursorPos(cm.getCursor(), Pos(0, 1)); 2147 cm.undoSelection(); 2148 eq(cm.getValue(), "abc"); 2149 }, {value: "abc"}); 2150 2151 testCM("selectionHistoryNonOverlapping", function(cm) { 2152 cm.setSelection(Pos(0, 0), Pos(0, 1)); 2153 cm.setSelection(Pos(0, 2), Pos(0, 3)); 2154 cm.execCommand("undoSelection"); 2155 eqCursorPos(cm.getCursor("anchor"), Pos(0, 0)); 2156 eqCursorPos(cm.getCursor("head"), Pos(0, 1)); 2157 }, {value: "1234"}); 2158 2159 testCM("cursorMotionSplitsHistory", function(cm) { 2160 cm.replaceSelection("a"); 2161 cm.execCommand("goCharRight"); 2162 cm.replaceSelection("b"); 2163 cm.replaceSelection("c"); 2164 cm.undo(); 2165 eq(cm.getValue(), "a1234"); 2166 eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); 2167 cm.undo(); 2168 eq(cm.getValue(), "1234"); 2169 eqCursorPos(cm.getCursor(), Pos(0, 0)); 2170 }, {value: "1234"}); 2171 2172 testCM("selChangeInOperationDoesNotSplit", function(cm) { 2173 for (var i = 0; i < 4; i++) { 2174 cm.operation(function() { 2175 cm.replaceSelection("x"); 2176 cm.setCursor(Pos(0, cm.getCursor().ch - 1)); 2177 }); 2178 } 2179 eqCursorPos(cm.getCursor(), Pos(0, 0)); 2180 eq(cm.getValue(), "xxxxa"); 2181 cm.undo(); 2182 eq(cm.getValue(), "a"); 2183 }, {value: "a"}); 2184 2185 testCM("alwaysMergeSelEventWithChangeOrigin", function(cm) { 2186 cm.replaceSelection("U", null, "foo"); 2187 cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "foo"}); 2188 cm.undoSelection(); 2189 eq(cm.getValue(), "a"); 2190 cm.replaceSelection("V", null, "foo"); 2191 cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "bar"}); 2192 cm.undoSelection(); 2193 eq(cm.getValue(), "Va"); 2194 }, {value: "a"}); 2195 2196 testCM("getTokenAt", function(cm) { 2197 var tokPlus = cm.getTokenAt(Pos(0, 2)); 2198 eq(tokPlus.type, "operator"); 2199 eq(tokPlus.string, "+"); 2200 var toks = cm.getLineTokens(0); 2201 eq(toks.length, 3); 2202 forEach([["number", "1"], ["operator", "+"], ["number", "2"]], function(expect, i) { 2203 eq(toks[i].type, expect[0]); 2204 eq(toks[i].string, expect[1]); 2205 }); 2206 }, {value: "1+2", mode: "javascript"}); 2207 2208 testCM("getTokenTypeAt", function(cm) { 2209 eq(cm.getTokenTypeAt(Pos(0, 0)), "number"); 2210 eq(cm.getTokenTypeAt(Pos(0, 6)), "string"); 2211 cm.addOverlay({ 2212 token: function(stream) { 2213 if (stream.match("foo")) return "foo"; 2214 else stream.next(); 2215 } 2216 }); 2217 eq(byClassName(cm.getWrapperElement(), "cm-foo").length, 1); 2218 eq(cm.getTokenTypeAt(Pos(0, 6)), "string"); 2219 }, {value: "1 + 'foo'", mode: "javascript"}); 2220 2221 testCM("addOverlay", function(cm) { 2222 cm.addOverlay({ 2223 token: function(stream) { 2224 var base = stream.baseToken() 2225 if (!/comment/.test(base.type) && stream.match(/\d+/)) return "x" 2226 stream.next() 2227 } 2228 }) 2229 var x = byClassName(cm.getWrapperElement(), "cm-x") 2230 is(x.length, 1) 2231 is(x[0].textContent, "233") 2232 cm.replaceRange("", Pos(0, 4), Pos(0, 6)) 2233 is(byClassName(cm.getWrapperElement(), "cm-x").length, 2) 2234 }, {value: "foo /* 100 */\nbar + 233;\nbaz", mode: "javascript"}) 2235 2236 testCM("resizeLineWidget", function(cm) { 2237 addDoc(cm, 200, 3); 2238 var widget = document.createElement("pre"); 2239 widget.innerHTML = "imwidget"; 2240 widget.style.background = "yellow"; 2241 cm.addLineWidget(1, widget, {noHScroll: true}); 2242 cm.setSize(40); 2243 is(widget.parentNode.offsetWidth < 42); 2244 }); 2245 2246 testCM("combinedOperations", function(cm) { 2247 var place = document.getElementById("testground"); 2248 var other = CodeMirror(place, {value: "123"}); 2249 try { 2250 cm.operation(function() { 2251 cm.addLineClass(0, "wrap", "foo"); 2252 other.addLineClass(0, "wrap", "foo"); 2253 }); 2254 eq(byClassName(cm.getWrapperElement(), "foo").length, 1); 2255 eq(byClassName(other.getWrapperElement(), "foo").length, 1); 2256 cm.operation(function() { 2257 cm.removeLineClass(0, "wrap", "foo"); 2258 other.removeLineClass(0, "wrap", "foo"); 2259 }); 2260 eq(byClassName(cm.getWrapperElement(), "foo").length, 0); 2261 eq(byClassName(other.getWrapperElement(), "foo").length, 0); 2262 } finally { 2263 place.removeChild(other.getWrapperElement()); 2264 } 2265 }, {value: "abc"}); 2266 2267 testCM("eventOrder", function(cm) { 2268 var seen = []; 2269 cm.on("change", function() { 2270 if (!seen.length) cm.replaceSelection("."); 2271 seen.push("change"); 2272 }); 2273 cm.on("cursorActivity", function() { 2274 cm.replaceSelection("!"); 2275 seen.push("activity"); 2276 }); 2277 cm.replaceSelection("/"); 2278 eq(seen.join(","), "change,change,activity,change"); 2279 }); 2280 2281 testCM("splitSpaces_nonspecial", function(cm) { 2282 eq(byClassName(cm.getWrapperElement(), "cm-invalidchar").length, 0); 2283 }, { 2284 specialChars: /[\u00a0]/, 2285 value: "spaces -> <- between" 2286 }); 2287 2288 test("core_rmClass", function() { 2289 var node = document.createElement("div"); 2290 node.className = "foo-bar baz-quux yadda"; 2291 CodeMirror.rmClass(node, "quux"); 2292 eq(node.className, "foo-bar baz-quux yadda"); 2293 CodeMirror.rmClass(node, "baz-quux"); 2294 eq(node.className, "foo-bar yadda"); 2295 CodeMirror.rmClass(node, "yadda"); 2296 eq(node.className, "foo-bar"); 2297 CodeMirror.rmClass(node, "foo-bar"); 2298 eq(node.className, ""); 2299 node.className = " foo "; 2300 CodeMirror.rmClass(node, "foo"); 2301 eq(node.className, ""); 2302 }); 2303 2304 test("core_addClass", function() { 2305 var node = document.createElement("div"); 2306 CodeMirror.addClass(node, "a"); 2307 eq(node.className, "a"); 2308 CodeMirror.addClass(node, "a"); 2309 eq(node.className, "a"); 2310 CodeMirror.addClass(node, "b"); 2311 eq(node.className, "a b"); 2312 CodeMirror.addClass(node, "a"); 2313 CodeMirror.addClass(node, "b"); 2314 eq(node.className, "a b"); 2315 }); 2316 2317 testCM("lineSeparator", function(cm) { 2318 eq(cm.lineCount(), 3); 2319 eq(cm.getLine(1), "bar\r"); 2320 eq(cm.getLine(2), "baz\rquux"); 2321 cm.setOption("lineSeparator", "\r"); 2322 eq(cm.lineCount(), 5); 2323 eq(cm.getLine(4), "quux"); 2324 eq(cm.getValue(), "foo\rbar\r\rbaz\rquux"); 2325 eq(cm.getValue("\n"), "foo\nbar\n\nbaz\nquux"); 2326 cm.setOption("lineSeparator", null); 2327 cm.setValue("foo\nbar\r\nbaz\rquux"); 2328 eq(cm.lineCount(), 4); 2329 }, {value: "foo\nbar\r\nbaz\rquux", 2330 lineSeparator: "\n"}); 2331 2332 var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ 2333 var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].indexOf(res.charCodeAt(0)) != -1 || (noExtending && extendingChars.test(res))); return res } 2334 var getString = function (n) { var res = getChar(true); while (--n > 0) res += getChar(); return res } 2335 2336 function makeItWrapAfter(cm, pos) { 2337 var firstLineTop = cm.cursorCoords(Pos(0, 0)).top; 2338 for(var w = 0, posTop; posTop != firstLineTop; ++w) { 2339 cm.setSize(w); 2340 posTop = cm.charCoords(pos).top; 2341 } 2342 } 2343 2344 function countIf(arr, f) { 2345 var result = 0 2346 for (var i = 0; i < arr.length; i++) if (f[arr[i]]) result++ 2347 return result 2348 } 2349 2350 function testMoveBidi(str) { 2351 testCM("move_bidi_" + str, function(cm) { 2352 if (cm.getOption("inputStyle") != "textarea" || !cm.getOption("rtlMoveVisually")) return; 2353 cm.getScrollerElement().style.fontFamily = "monospace"; 2354 makeItWrapAfter(cm, Pos(0, 5)); 2355 2356 var steps = str.length - countIf(str.split(""), function(ch) { return extendingChars.test(ch) }); 2357 var lineBreaks = {} 2358 lineBreaks[6 - countIf(str.substr(0, 5).split(""), function(ch) { return extendingChars.test(ch) })] = 'w'; 2359 if (str.indexOf("\n") != -1) { 2360 lineBreaks[steps - 2] = 'n'; 2361 } 2362 2363 // Make sure we are at the visual beginning of the first line 2364 cm.execCommand("goLineStart"); 2365 2366 var prevCoords = cm.cursorCoords(), coords; 2367 for(var i = 0; i < steps; ++i) { 2368 cm.execCommand("goCharRight"); 2369 coords = cm.cursorCoords(); 2370 if ((i >= 10 && i <= 12) && !lineBreaks[i] && coords.left < prevCoords.left && coords.top > prevCoords.top) { 2371 // The first line wraps twice 2372 lineBreaks[i] = 'w'; 2373 } 2374 if (!lineBreaks[i]) { 2375 is(coords.left > prevCoords.left, "In step " + i + ", cursor didn't move right"); 2376 eq(coords.top, prevCoords.top, "In step " + i + ", cursor moved out of line"); 2377 } else { 2378 is(coords.left < prevCoords.left, i); 2379 is(coords.top > prevCoords.top, i); 2380 } 2381 prevCoords = coords; 2382 } 2383 2384 cm.execCommand("goCharRight"); 2385 coords = cm.cursorCoords(); 2386 eq(coords.left, prevCoords.left, "Moving " + steps + " steps right didn't reach the end"); 2387 eq(coords.top, prevCoords.top, "Moving " + steps + " steps right didn't reach the end"); 2388 2389 for(i = steps - 1; i >= 0; --i) { 2390 cm.execCommand("goCharLeft"); 2391 coords = cm.cursorCoords(); 2392 if (!(lineBreaks[i] == 'n' || lineBreaks[i + 1] == 'w')) { 2393 is(coords.left < prevCoords.left, "In step " + i + ", cursor didn't move left"); 2394 eq(coords.top, prevCoords.top, "In step " + i + ", cursor is not at the same line anymore"); 2395 } else { 2396 is(coords.left > prevCoords.left, i); 2397 is(coords.top < prevCoords.top, i); 2398 } 2399 prevCoords = coords; 2400 } 2401 2402 cm.execCommand("goCharLeft"); 2403 coords = cm.cursorCoords(); 2404 eq(coords.left, prevCoords.left, "Moving " + steps + " steps left didn't reach the beginning"); 2405 eq(coords.top, prevCoords.top, "Moving " + steps + " steps left didn't reach the beginning"); 2406 }, {value: str, lineWrapping: true}) 2407 }; 2408 2409 function testMoveEndBidi(str) { 2410 testCM("move_end_bidi_" + str, function(cm) { 2411 cm.getScrollerElement().style.fontFamily = "monospace"; 2412 makeItWrapAfter(cm, Pos(0, 5)); 2413 2414 cm.execCommand("goLineStart"); 2415 var pos = cm.doc.getCursor(); 2416 cm.execCommand("goCharLeft"); 2417 eqCursorPos(pos, cm.doc.getCursor()); 2418 2419 cm.execCommand("goLineEnd"); 2420 pos = cm.doc.getCursor(); 2421 cm.execCommand("goColumnRight"); 2422 eqCursorPos(pos, cm.doc.getCursor()); 2423 }, {value: str, lineWrapping: true}) 2424 }; 2425 2426 var bidiTests = []; 2427 2428 // We don't correctly implement L1 UBA 2429 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1331501 2430 // and https://bugs.chromium.org/p/chromium/issues/detail?id=673405 2431 /* 2432 bidiTests.push("Say ا ب جabj\nS"); 2433 bidiTests.push("Sayyy ا ا ب ج"); 2434 */ 2435 2436 if (!phantom) { 2437 bidiTests.push("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); 2438 bidiTests.push("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ"); 2439 bidiTests.push("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); 2440 bidiTests.push("؅؁ĆՕƿɁǞϮؠȩóć\nď"); 2441 bidiTests.push("RŨďңŪzϢŎƏԖڇڦ\nӈ"); 2442 bidiTests.push("ό׊۷٢ԜһОצЉيčǟ\nѩ"); 2443 bidiTests.push("ۑÚҳҕڬġڹհяųKV\nr"); 2444 bidiTests.push("źڻғúہ4ם1Ƞc1a\nԁ"); 2445 bidiTests.push("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); 2446 bidiTests.push("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); 2447 bidiTests.push("۹ؼL۵ĺȧКԙػא7״\nم"); 2448 bidiTests.push("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior 2449 } 2450 2451 bidiTests.push("քմѧǮßپüŢҍҞўڳ\nӧ"); 2452 2453 //bidiTests.push("Count ١ ٢ ٣ ٤"); 2454 //bidiTests.push("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); 2455 //bidiTests.push("ҾճٳџIՖӻ٥׭֐؜ڏ\nێ"); 2456 //bidiTests.push("ҬÓФ؜ڂį٦Ͽɓڐͳٵ\nՈ"); 2457 //bidiTests.push("aѴNijȻهˇ҃ڱӧǻֵ\na"); 2458 //bidiTests.push(" a٧ا٢ ب جa\nS"); 2459 2460 for (var i = 0; i < bidiTests.length; ++i) { 2461 testMoveBidi(bidiTests[i]); 2462 testMoveEndBidi(bidiTests[i]); 2463 } 2464 2465 /* 2466 for (var i = 0; i < 5; ++i) { 2467 testMoveBidi(getString(12) + "\n" + getString(1)); 2468 } 2469 */ 2470 2471 function testCoordsWrappedBidi(str) { 2472 testCM("coords_wrapped_bidi_" + str, function(cm) { 2473 cm.getScrollerElement().style.fontFamily = "monospace"; 2474 makeItWrapAfter(cm, Pos(0, 5)); 2475 2476 // Make sure we are at the visual beginning of the first line 2477 var pos = Pos(0, 0), lastPos; 2478 cm.doc.setCursor(pos); 2479 do { 2480 lastPos = pos; 2481 cm.execCommand("goCharLeft"); 2482 pos = cm.doc.getCursor(); 2483 } while (pos != lastPos) 2484 2485 var top = cm.charCoords(Pos(0, 0)).top, lastTop; 2486 for (var i = 1; i < str.length; ++i) { 2487 lastTop = top; 2488 top = cm.charCoords(Pos(0, i)).top; 2489 is(top >= lastTop); 2490 } 2491 }, {value: str, lineWrapping: true}) 2492 }; 2493 2494 testCoordsWrappedBidi("Count ١ ٢ ٣ ٤"); 2495 /* 2496 for (var i = 0; i < 5; ++i) { 2497 testCoordsWrappedBidi(getString(50)); 2498 } 2499 */ 2500 2501 testCM("rtl_wrapped_selection", function(cm) { 2502 cm.setSelection(Pos(0, 10), Pos(0, 190)) 2503 is(byClassName(cm.getWrapperElement(), "CodeMirror-selected").length >= 3) 2504 }, {value: new Array(10).join(" فتي تم تضمينها فتي تم"), lineWrapping: true}) 2505 2506 testCM("bidi_wrapped_selection", function(cm) { 2507 if (phantom) return 2508 cm.setSize(cm.charCoords(Pos(0, 10), "editor").left) 2509 cm.setSelection(Pos(0, 37), Pos(0, 80)) 2510 var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected") 2511 is(blocks.length >= 2) 2512 is(blocks.length <= 3) 2513 var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[blocks.length - 1].getBoundingClientRect() 2514 is(boxTop.left > cm.charCoords(Pos(0, 1)).right) 2515 is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left) 2516 }, {value: "<p>مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت</p>", lineWrapping: true}) 2517 2518 testCM("delete_wrapped", function(cm) { 2519 makeItWrapAfter(cm, Pos(0, 2)); 2520 cm.doc.setCursor(Pos(0, 3, "after")); 2521 cm.deleteH(-1, "char"); 2522 eq(cm.getLine(0), "1245"); 2523 }, {value: "12345", lineWrapping: true}) 2524 2525 testCM("issue_4878", function(cm) { 2526 if (phantom) return 2527 cm.setCursor(Pos(1, 12, "after")); 2528 cm.moveH(-1, "char"); 2529 eqCursorPos(cm.getCursor(), Pos(0, 113, "before")); 2530 }, {value: " في تطبيق السمات مرة واحدة https://github.com/codemirror/CodeMirror/issues/4878#issuecomment-330550964على سبيل المثال <code>\"foo bar\"</code>\n" + 2531 " سيتم تعيين", direction: "rtl", lineWrapping: true}); 2532 2533 CodeMirror.defineMode("lookahead_mode", function() { 2534 // Colors text as atom if the line two lines down has an x in it 2535 return { 2536 token: function(stream) { 2537 stream.skipToEnd() 2538 return /x/.test(stream.lookAhead(2)) ? "atom" : null 2539 } 2540 } 2541 }) 2542 2543 testCM("mode_lookahead", function(cm) { 2544 eq(cm.getTokenAt(Pos(0, 1)).type, "atom") 2545 eq(cm.getTokenAt(Pos(1, 1)).type, "atom") 2546 eq(cm.getTokenAt(Pos(2, 1)).type, null) 2547 cm.replaceRange("\n", Pos(2, 0)) 2548 eq(cm.getTokenAt(Pos(0, 1)).type, null) 2549 eq(cm.getTokenAt(Pos(1, 1)).type, "atom") 2550 }, {value: "foo\na\nx\nx\n", mode: "lookahead_mode"})
Download modules/editor/codemirror/test/test.min.js
History Tue, 22 May 2018 22:39:55 +0200 Jan Dankert Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.