public/lib/CodeMirror/test/test.js   F
last analyzed

Complexity

Total Complexity 287
Complexity/F 1.44

Size

Lines of Code 2142
Function Count 199

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1823
nc 1610612736
dl 0
loc 2142
rs 0.8
c 0
b 0
f 0
wmc 287
mnd 4
bc 242
fnc 199
bpm 1.216
cpm 1.4422
noi 57

127 Functions

Rating   Name   Duplication   Size   Complexity  
A test.js ➔ testCM(ꞌsetValueClearsꞌ) 0 7 1
A test.js ➔ test(ꞌcore_defaultsꞌ) 0 21 3
A test.js ➔ testCM(ꞌextraKeysꞌ) 0 29 1
A test.js ➔ testCM(ꞌundoDoesntClearValueꞌ) 0 4 1
A test.js ➔ testCM(ꞌverticalMovementCommandsꞌ) 0 22 2
A test.js ➔ test(ꞌcore_rmClassꞌ) 0 15 1
A test.js ➔ testCM(ꞌgetTokenTypeAtꞌ) 0 12 1
A test.js ➔ testCM(ꞌcoordsCharꞌ) 0 14 5
A test.js ➔ testCM(ꞌchangedInlineWidgetꞌ) 0 10 1
A test.js ➔ testCM(ꞌmarkTextMultiLineꞌ) 0 25 1
A test.js ➔ testCM(ꞌaddKeyMapꞌ) 0 31 1
A test.js ➔ testCM(ꞌmeasureEndOfLineꞌ) 0 18 4
A test.js ➔ testCM(ꞌselectionꞌ) 0 21 1
A test.js ➔ testCM(ꞌbug577ꞌ) 0 6 1
A test.js ➔ testCM(ꞌdoubleScrollbarꞌ) 0 11 2
A test.js ➔ testCM(ꞌwordMovementCommandsꞌ) 0 20 1
A test.js ➔ testCM(ꞌsetSizeꞌ) 0 12 1
A test.js ➔ testCM(ꞌselectionHistoryꞌ) 0 24 2
A test.js ➔ testCM(ꞌbeforeChangeꞌ) 0 17 1
A test.js ➔ testCM(ꞌundoPreservesNewMarksꞌ) 0 18 1
A test.js ➔ testCM(ꞌgetRangeꞌ) 0 10 1
A test.js ➔ testCM(ꞌreadOnlyMarkerꞌ) 0 34 1
A test.js ➔ testCM(ꞌgetAllMarksꞌ) 0 11 1
A test.js ➔ testCM(ꞌlineChangeEventsꞌ) 0 3 1
A test.js ➔ testCM(ꞌweirdLinebreaksꞌ) 0 7 1
A test.js ➔ testCM(ꞌselectionHistoryNonOverlappingꞌ) 0 7 1
A test.js ➔ testCM(ꞌrandomCollapsedRangesꞌ) 0 13 1
A test.js ➔ testCM(ꞌbookmarkInsertLeftꞌ) 0 14 1
A test.js ➔ testCM(ꞌlineInfoꞌ) 0 17 1
A test.js ➔ testCM(ꞌmovebyTextUnitꞌ) 0 13 2
A test.js ➔ testCM(ꞌselectAllNoScrollꞌ) 0 8 1
A test.js ➔ testCM(ꞌinlineWidgetꞌ) 0 13 1
A test.js ➔ testCM(ꞌbookmarkCursorꞌ) 0 21 1
A test.js ➔ testCM(ꞌbeforeChangeUndoꞌ) 0 12 1
A test.js ➔ testCM(ꞌverticalScrollꞌ) 0 16 2
A test.js ➔ testCM(ꞌlineWidgetFocusꞌ) 0 15 1
A test.js ➔ testCM(ꞌbadNestedFoldꞌ) 0 9 1
A test.js ➔ testCM(ꞌcombinedOperationsꞌ) 0 20 1
A CodeMirror.registerGlobalHelper(ꞌxxxꞌ) 0 1 1
A test.js ➔ testCM(ꞌscrollBackAndForthꞌ) 0 8 1
A test.js ➔ testCM(ꞌscrollSnapꞌ) 0 14 1
A test.js ➔ testCM(ꞌgroupMovementCommandsꞌ) 0 33 1
A test.js ➔ testCM(ꞌmoveVstuckꞌ) 0 12 3
A test.js ➔ testCM(ꞌgroupsAndWhitespaceꞌ) 0 12 4
A test.js ➔ testCM(ꞌcoordsꞌ) 0 13 1
A test.js ➔ testCM(ꞌlinesꞌ) 0 8 1
A test.js ➔ testCM(ꞌscrollIntoViewꞌ) 0 20 2
A test.js ➔ testCM(ꞌresizeLineWidgetꞌ) 0 9 1
A test.js ➔ testCM(ꞌlineStyleFromModeꞌ) 0 26 1
A test.js ➔ testCM(ꞌeditInFoldꞌ) 0 8 1
A test.js ➔ testCM(ꞌnestedFoldꞌ) 0 26 1
A test.js ➔ testCM(ꞌlineWidgetChangedꞌ) 0 62 1
A test.js ➔ testCM(ꞌchange_removedTextꞌ) 0 29 1
A test.js ➔ addDoc 0 6 3
A test.js ➔ testCM(ꞌundoSelectionꞌ) 0 12 1
A test.js ➔ testCM(ꞌmarkTextUndoꞌ) 0 27 1
A test.js ➔ testCM(ꞌdirtyBitꞌ) 0 17 1
A test.js ➔ testCM(ꞌposFromIndexꞌ) 0 26 4
A test.js ➔ testCM(ꞌgetTokenAtꞌ) 0 11 1
A test.js ➔ testCM(ꞌcharMovementCommandsꞌ) 0 24 1
A test.js ➔ testCM(ꞌselectionHomeEndꞌ) 0 9 1
A test.js ➔ scrollbarWidth 0 9 2
A test.js ➔ testCM(ꞌjumpTheGapꞌ) 0 30 2
A test.js ➔ testCM(ꞌmarkTextStayGoneꞌ) 0 7 1
A test.js ➔ testCM(ꞌreplaceRangeꞌ) 0 13 1
A test.js ➔ testCM(ꞌchangedBookmarkꞌ) 0 10 1
A test.js ➔ testCM(ꞌmarkTextAllowEmptyꞌ) 0 22 1
A test.js ➔ testCM(ꞌindentꞌ) 0 11 1
A test.js ➔ testCM(ꞌcollapsedLinesꞌ) 0 18 1
B test.js ➔ testCM(ꞌselectionPosꞌ) 0 34 8
A test.js ➔ testCM(ꞌselectionChangeConfusesHistoryꞌ) 0 8 1
A test.js ➔ testCM(ꞌnestedFoldOnSideꞌ) 0 16 1
A test.js ➔ testCM(ꞌhiddenLinesAutoUnfoldꞌ) 0 14 1
A test.js ➔ testCM(ꞌcollapsedRangeBetweenLinesSelectedꞌ) 0 11 3
A test.js ➔ testCM(ꞌcollapsedRangeCoordsCharꞌ) 0 14 1
A test.js ➔ testCM(ꞌeverythingFoldedꞌ) 0 14 1
A test.js ➔ testCM(ꞌshowEmptyWidgetSpanꞌ) 0 7 1
A test.js ➔ testCM(ꞌhelpersꞌ) 0 8 1
A test.js ➔ testCM(ꞌchangeGenerationꞌ) 0 14 1
A test.js ➔ testCM(ꞌundoDepthꞌ) 0 7 1
A test.js ➔ testCM(ꞌfindPosHꞌ) 0 20 1
A test.js ➔ foldLines 0 8 1
A test.js ➔ testCM(ꞌselectionBiasꞌ) 0 12 1
A test.js ➔ testCM(ꞌverticalMovementCommandsWrappingꞌ) 0 14 4
A test.js ➔ testCM(ꞌsplitSpaces_nonspecialꞌ) 0 3 1
A test.js ➔ testCM(ꞌlineWidgetsꞌ) 0 13 1
A test.js ➔ testCM(ꞌlineSeparatorꞌ) 0 13 1
A test.js ➔ byClassName 0 12 2
B test.js ➔ testCM(ꞌaddLineClassꞌ) 0 43 1
A test.js ➔ testCM(ꞌhiddenLinesSelectAllꞌ) 0 8 1
A test.js ➔ testCM(ꞌmarkTextCSSꞌ) 0 10 1
A test.js ➔ testCM(ꞌundoCompositeꞌ) 0 14 1
A test.js ➔ testCM(ꞌselectionChangeReducesRedoꞌ) 0 11 1
A test.js ➔ testCM(ꞌscrollVerticallyAndHorizontallyꞌ) 0 12 2
A test.js ➔ testCM(ꞌmultiBookmarkCursorꞌ) 0 17 3
A test.js ➔ testCM(ꞌcursorMotionSplitsHistoryꞌ) 0 12 1
A test.js ➔ testCM(ꞌcollapseOnMoveꞌ) 0 17 1
A test.js ➔ testCM(ꞌmarkClearBetweenꞌ) 0 6 1
A test.js ➔ testCM(ꞌbeforeSelectionChangeꞌ) 0 18 1
A test.js ➔ testCM(ꞌgetLineNumberꞌ) 0 9 1
A test.js ➔ testCM(ꞌdeleteSpanCollapsedInclusiveLeftꞌ) 0 6 1
A test.js ➔ test(ꞌcore_fromTextAreaꞌ) 0 15 1
A test.js ➔ testCM(ꞌscrollEntirelyToRightꞌ) 0 7 3
A test.js ➔ testCM(ꞌmarkTextSingleLineꞌ) 0 22 1
A test.js ➔ testCM(ꞌundoSelectionAsBeforeꞌ) 0 6 1
B test.js ➔ testCM(ꞌwrappingAndResizingꞌ) 0 31 5
A test.js ➔ test(ꞌcore_addClassꞌ) 0 12 1
A test.js ➔ testCM(ꞌmarkTextStackedꞌ) 0 6 1
B test.js ➔ testCM(ꞌatomicMarkerꞌ) 0 47 1
A test.js ➔ testCM(ꞌrestoreHistoryꞌ) 0 18 2
A test.js ➔ forEach 0 3 2
A test.js ➔ testCM(ꞌwrappingInlineWidgetꞌ) 0 20 2
A test.js ➔ testCM(ꞌselChangeInOperationDoesNotSplitꞌ) 0 12 2
B test.js ➔ testCM(ꞌstructuredFoldꞌ) 0 43 2
A test.js ➔ testCM(ꞌlineStyleFromBlankLineꞌ) 0 13 1
A test.js ➔ testCM(ꞌundoMultiLineꞌ) 0 21 1
A test.js ➔ testCM(ꞌclickTabꞌ) 0 5 1
A CodeMirror.defineMode(ꞌyyyꞌ) 0 6 1
A test.js ➔ testCM(ꞌextendSelectionꞌ) 0 36 1
A test.js ➔ testCM(ꞌeventOrderꞌ) 0 13 1
A test.js ➔ testCM(ꞌbookmarkꞌ) 0 16 1
A test.js ➔ testCM(ꞌlineWidgetCautiousRedrawꞌ) 0 9 1
A test.js ➔ testCM(ꞌindentByNumberꞌ) 0 9 1
A test.js ➔ testCM(ꞌalwaysMergeSelEventWithChangeOriginꞌ) 0 10 1
A test.js ➔ testCM(ꞌundoꞌ) 0 24 3
A test.js ➔ testCM(ꞌrtlMovementꞌ) 0 27 2
A test.js ➔ testCM(ꞌbidiUpdateꞌ) 0 6 1

How to fix   Complexity   

Complexity

Complex classes like public/lib/CodeMirror/test/test.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
var Pos = CodeMirror.Pos;
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 11. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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_";
0 ignored issues
show
Bug introduced by
The variable namespace seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.namespace.
Loading history...
39
40
test("core_fromTextArea", function() {
41
  var te = document.getElementById("code");
42
  te.value = "CONTENT";
43
  var cm = CodeMirror.fromTextArea(te);
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
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
  eqPos(cm.getCursor(false), Pos(2, 2));
86
  eqPos(cm.getCursor(true), Pos(0, 4));
87
  cm.setSelection(Pos(1, 0));
88
  is(!cm.somethingSelected());
89
  eq(cm.getSelection(), "");
90
  eqPos(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
  eqPos(cm.getCursor(true), Pos(1, 3));
97
  cm.setCursor(Pos(2, 1));
98
  eqPos(cm.getCursor(true), Pos(2, 1));
99
  cm.setCursor(1, 2);
100
  eqPos(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
  eqPos(cm.getCursor("head"), Pos(3, 5));
108
  eqPos(cm.getCursor("anchor"), Pos(3, 5));
109
  cm.setSelection(Pos(2, 5), Pos(5, 5));
110
  eqPos(cm.getCursor("head"), Pos(5, 5));
111
  eqPos(cm.getCursor("anchor"), Pos(2, 5));
112
  eqPos(cm.getCursor("start"), Pos(2, 5));
113
  eqPos(cm.getCursor("end"), Pos(5, 5));
114
  cm.setSelection(Pos(5, 5), Pos(2, 5));
115
  eqPos(cm.getCursor("head"), Pos(2, 5));
116
  eqPos(cm.getCursor("anchor"), Pos(5, 5));
117
  eqPos(cm.getCursor("start"), Pos(2, 5));
118
  eqPos(cm.getCursor("end"), Pos(5, 5));
119
  cm.extendSelection(Pos(3, 2));
120
  eqPos(cm.getCursor("head"), Pos(3, 2));
121
  eqPos(cm.getCursor("anchor"), Pos(5, 5));
122
  cm.extendSelection(Pos(6, 2));
123
  eqPos(cm.getCursor("head"), Pos(6, 2));
124
  eqPos(cm.getCursor("anchor"), Pos(5, 5));
125
  cm.extendSelection(Pos(6, 3), Pos(6, 4));
126
  eqPos(cm.getCursor("head"), Pos(6, 4));
127
  eqPos(cm.getCursor("anchor"), Pos(5, 5));
128
  cm.extendSelection(Pos(0, 3), Pos(0, 4));
129
  eqPos(cm.getCursor("head"), Pos(0, 3));
130
  eqPos(cm.getCursor("anchor"), Pos(5, 5));
131
  cm.extendSelection(Pos(4, 5), Pos(6, 5));
132
  eqPos(cm.getCursor("head"), Pos(6, 5));
133
  eqPos(cm.getCursor("anchor"), Pos(4, 5));
134
  cm.setExtending(false);
135
  cm.extendSelection(Pos(0, 3), Pos(0, 4));
136
  eqPos(cm.getCursor("head"), Pos(0, 3));
137
  eqPos(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;
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
173
  for (var opt in defs) defsCopy[opt] = defs[opt];
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
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];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable opt already seems to be declared on line 173. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
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
        eqPos(pos, Pos(line, ch));
235
      }
236
    }
237
  }
238
}, {lineNumbers: true});
239
240
testCM("posFromIndex", function(cm) {
241
  cm.setValue(
242
    "This function should\n" +
243
    "convert a zero based index\n" +
244
    "to line and ch."
245
  );
246
247
  var examples = [
248
    { index: -1, line: 0, ch: 0  }, // <- Tests clipping
249
    { index: 0,  line: 0, ch: 0  },
250
    { index: 10, line: 0, ch: 10 },
251
    { index: 39, line: 1, ch: 18 },
252
    { index: 55, line: 2, ch: 7  },
253
    { index: 63, line: 2, ch: 15 },
254
    { index: 64, line: 2, ch: 15 }  // <- Tests clipping
255
  ];
256
257
  for (var i = 0; i < examples.length; i++) {
258
    var example = examples[i];
259
    var pos = cm.posFromIndex(example.index);
260
    eq(pos.line, example.line);
261
    eq(pos.ch, example.ch);
262
    if (example.index >= 0 && example.index < 64)
263
      eq(cm.indexFromPos(pos), example.index);
264
  }
265
});
266
267
testCM("undo", function(cm) {
268
  cm.replaceRange("def", Pos(0, 0), Pos(0));
269
  eq(cm.historySize().undo, 1);
270
  cm.undo();
271
  eq(cm.getValue(), "abc");
272
  eq(cm.historySize().undo, 0);
273
  eq(cm.historySize().redo, 1);
274
  cm.redo();
275
  eq(cm.getValue(), "def");
276
  eq(cm.historySize().undo, 1);
277
  eq(cm.historySize().redo, 0);
278
  cm.setValue("1\n\n\n2");
279
  cm.clearHistory();
280
  eq(cm.historySize().undo, 0);
281
  for (var i = 0; i < 20; ++i) {
282
    cm.replaceRange("a", Pos(0, 0));
283
    cm.replaceRange("b", Pos(3, 0));
284
  }
285
  eq(cm.historySize().undo, 40);
286
  for (var i = 0; i < 40; ++i)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 281. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
287
    cm.undo();
288
  eq(cm.historySize().redo, 40);
289
  eq(cm.getValue(), "1\n\n\n2");
290
}, {value: "abc"});
291
292
testCM("undoDepth", function(cm) {
293
  cm.replaceRange("d", Pos(0));
294
  cm.replaceRange("e", Pos(0));
295
  cm.replaceRange("f", Pos(0));
296
  cm.undo(); cm.undo(); cm.undo();
297
  eq(cm.getValue(), "abcd");
298
}, {value: "abc", undoDepth: 4});
299
300
testCM("undoDoesntClearValue", function(cm) {
301
  cm.undo();
302
  eq(cm.getValue(), "x");
303
}, {value: "x"});
304
305
testCM("undoMultiLine", function(cm) {
306
  cm.operation(function() {
307
    cm.replaceRange("x", Pos(0, 0));
308
    cm.replaceRange("y", Pos(1, 0));
309
  });
310
  cm.undo();
311
  eq(cm.getValue(), "abc\ndef\nghi");
312
  cm.operation(function() {
313
    cm.replaceRange("y", Pos(1, 0));
314
    cm.replaceRange("x", Pos(0, 0));
315
  });
316
  cm.undo();
317
  eq(cm.getValue(), "abc\ndef\nghi");
318
  cm.operation(function() {
319
    cm.replaceRange("y", Pos(2, 0));
320
    cm.replaceRange("x", Pos(1, 0));
321
    cm.replaceRange("z", Pos(2, 0));
322
  });
323
  cm.undo();
324
  eq(cm.getValue(), "abc\ndef\nghi", 3);
325
}, {value: "abc\ndef\nghi"});
326
327
testCM("undoComposite", function(cm) {
328
  cm.replaceRange("y", Pos(1));
329
  cm.operation(function() {
330
    cm.replaceRange("x", Pos(0));
331
    cm.replaceRange("z", Pos(2));
332
  });
333
  eq(cm.getValue(), "ax\nby\ncz\n");
334
  cm.undo();
335
  eq(cm.getValue(), "a\nby\nc\n");
336
  cm.undo();
337
  eq(cm.getValue(), "a\nb\nc\n");
338
  cm.redo(); cm.redo();
339
  eq(cm.getValue(), "ax\nby\ncz\n");
340
}, {value: "a\nb\nc\n"});
341
342
testCM("undoSelection", function(cm) {
343
  cm.setSelection(Pos(0, 2), Pos(0, 4));
344
  cm.replaceSelection("");
345
  cm.setCursor(Pos(1, 0));
346
  cm.undo();
347
  eqPos(cm.getCursor(true), Pos(0, 2));
348
  eqPos(cm.getCursor(false), Pos(0, 4));
349
  cm.setCursor(Pos(1, 0));
350
  cm.redo();
351
  eqPos(cm.getCursor(true), Pos(0, 2));
352
  eqPos(cm.getCursor(false), Pos(0, 2));
353
}, {value: "abcdefgh\n"});
354
355
testCM("undoSelectionAsBefore", function(cm) {
356
  cm.replaceSelection("abc", "around");
357
  cm.undo();
358
  cm.redo();
359
  eq(cm.getSelection(), "abc");
360
});
361
362
testCM("selectionChangeConfusesHistory", function(cm) {
363
  cm.replaceSelection("abc", null, "dontmerge");
364
  cm.operation(function() {
365
    cm.setCursor(Pos(0, 0));
366
    cm.replaceSelection("abc", null, "dontmerge");
367
  });
368
  eq(cm.historySize().undo, 2);
369
});
370
371
testCM("markTextSingleLine", function(cm) {
372
  forEach([{a: 0, b: 1, c: "", f: 2, t: 5},
373
           {a: 0, b: 4, c: "", f: 0, t: 2},
374
           {a: 1, b: 2, c: "x", f: 3, t: 6},
375
           {a: 4, b: 5, c: "", f: 3, t: 5},
376
           {a: 4, b: 5, c: "xx", f: 3, t: 7},
377
           {a: 2, b: 5, c: "", f: 2, t: 3},
378
           {a: 2, b: 5, c: "abcd", f: 6, t: 7},
379
           {a: 2, b: 6, c: "x", f: null, t: null},
380
           {a: 3, b: 6, c: "", f: null, t: null},
381
           {a: 0, b: 9, c: "hallo", f: null, t: null},
382
           {a: 4, b: 6, c: "x", f: 3, t: 4},
383
           {a: 4, b: 8, c: "", f: 3, t: 4},
384
           {a: 6, b: 6, c: "a", f: 3, t: 6},
385
           {a: 8, b: 9, c: "", f: 3, t: 6}], function(test) {
386
    cm.setValue("1234567890");
387
    var r = cm.markText(Pos(0, 3), Pos(0, 6), {className: "foo"});
388
    cm.replaceRange(test.c, Pos(0, test.a), Pos(0, test.b));
389
    var f = r.find();
390
    eq(f && f.from.ch, test.f); eq(f && f.to.ch, test.t);
391
  });
392
});
393
394
testCM("markTextMultiLine", function(cm) {
395
  function p(v) { return v && Pos(v[0], v[1]); }
396
  forEach([{a: [0, 0], b: [0, 5], c: "", f: [0, 0], t: [2, 5]},
397
           {a: [0, 0], b: [0, 5], c: "foo\n", f: [1, 0], t: [3, 5]},
398
           {a: [0, 1], b: [0, 10], c: "", f: [0, 1], t: [2, 5]},
399
           {a: [0, 5], b: [0, 6], c: "x", f: [0, 6], t: [2, 5]},
400
           {a: [0, 0], b: [1, 0], c: "", f: [0, 0], t: [1, 5]},
401
           {a: [0, 6], b: [2, 4], c: "", f: [0, 5], t: [0, 7]},
402
           {a: [0, 6], b: [2, 4], c: "aa", f: [0, 5], t: [0, 9]},
403
           {a: [1, 2], b: [1, 8], c: "", f: [0, 5], t: [2, 5]},
404
           {a: [0, 5], b: [2, 5], c: "xx", f: null, t: null},
405
           {a: [0, 0], b: [2, 10], c: "x", f: null, t: null},
406
           {a: [1, 5], b: [2, 5], c: "", f: [0, 5], t: [1, 5]},
407
           {a: [2, 0], b: [2, 3], c: "", f: [0, 5], t: [2, 2]},
408
           {a: [2, 5], b: [3, 0], c: "a\nb", f: [0, 5], t: [2, 5]},
409
           {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 3]},
410
           {a: [1, 1], b: [1, 9], c: "1\n2\n3", f: [0, 5], t: [4, 5]}], function(test) {
411
    cm.setValue("aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\ndddddddd\n");
412
    var r = cm.markText(Pos(0, 5), Pos(2, 5),
413
                        {className: "CodeMirror-matchingbracket"});
414
    cm.replaceRange(test.c, p(test.a), p(test.b));
415
    var f = r.find();
416
    eqPos(f && f.from, p(test.f)); eqPos(f && f.to, p(test.t));
417
  });
418
});
419
420
testCM("markTextUndo", function(cm) {
421
  var marker1, marker2, bookmark;
422
  marker1 = cm.markText(Pos(0, 1), Pos(0, 3),
423
                        {className: "CodeMirror-matchingbracket"});
424
  marker2 = cm.markText(Pos(0, 0), Pos(2, 1),
425
                        {className: "CodeMirror-matchingbracket"});
426
  bookmark = cm.setBookmark(Pos(1, 5));
427
  cm.operation(function(){
428
    cm.replaceRange("foo", Pos(0, 2));
429
    cm.replaceRange("bar\nbaz\nbug\n", Pos(2, 0), Pos(3, 0));
430
  });
431
  var v1 = cm.getValue();
432
  cm.setValue("");
433
  eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null);
434
  cm.undo();
435
  eqPos(bookmark.find(), Pos(1, 5), "still there");
436
  cm.undo();
437
  var m1Pos = marker1.find(), m2Pos = marker2.find();
438
  eqPos(m1Pos.from, Pos(0, 1)); eqPos(m1Pos.to, Pos(0, 3));
439
  eqPos(m2Pos.from, Pos(0, 0)); eqPos(m2Pos.to, Pos(2, 1));
440
  eqPos(bookmark.find(), Pos(1, 5));
441
  cm.redo(); cm.redo();
442
  eq(bookmark.find(), null);
443
  cm.undo();
444
  eqPos(bookmark.find(), Pos(1, 5));
445
  eq(cm.getValue(), v1);
446
}, {value: "1234\n56789\n00\n"});
447
448
testCM("markTextStayGone", function(cm) {
449
  var m1 = cm.markText(Pos(0, 0), Pos(0, 1));
450
  cm.replaceRange("hi", Pos(0, 2));
451
  m1.clear();
452
  cm.undo();
453
  eq(m1.find(), null);
454
}, {value: "hello"});
455
456
testCM("markTextAllowEmpty", function(cm) {
457
  var m1 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false});
458
  is(m1.find());
459
  cm.replaceRange("x", Pos(0, 0));
460
  is(m1.find());
461
  cm.replaceRange("y", Pos(0, 2));
462
  is(m1.find());
463
  cm.replaceRange("z", Pos(0, 3), Pos(0, 4));
464
  is(!m1.find());
465
  var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false,
466
                                              inclusiveLeft: true,
467
                                              inclusiveRight: true});
468
  cm.replaceRange("q", Pos(0, 1), Pos(0, 2));
469
  is(m2.find());
470
  cm.replaceRange("", Pos(0, 0), Pos(0, 3));
471
  is(!m2.find());
472
  var m3 = cm.markText(Pos(0, 1), Pos(0, 1), {clearWhenEmpty: false});
473
  cm.replaceRange("a", Pos(0, 3));
474
  is(m3.find());
475
  cm.replaceRange("b", Pos(0, 1));
476
  is(!m3.find());
477
}, {value: "abcde"});
478
479
testCM("markTextStacked", function(cm) {
480
  var m1 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
481
  var m2 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
482
  cm.replaceRange("B", Pos(0, 1));
483
  is(m1.find() && m2.find());
484
}, {value: "A"});
485
486
testCM("undoPreservesNewMarks", function(cm) {
487
  cm.markText(Pos(0, 3), Pos(0, 4));
488
  cm.markText(Pos(1, 1), Pos(1, 3));
489
  cm.replaceRange("", Pos(0, 3), Pos(3, 1));
490
  var mBefore = cm.markText(Pos(0, 0), Pos(0, 1));
491
  var mAfter = cm.markText(Pos(0, 5), Pos(0, 6));
492
  var mAround = cm.markText(Pos(0, 2), Pos(0, 4));
493
  cm.undo();
494
  eqPos(mBefore.find().from, Pos(0, 0));
495
  eqPos(mBefore.find().to, Pos(0, 1));
496
  eqPos(mAfter.find().from, Pos(3, 3));
497
  eqPos(mAfter.find().to, Pos(3, 4));
498
  eqPos(mAround.find().from, Pos(0, 2));
499
  eqPos(mAround.find().to, Pos(3, 2));
500
  var found = cm.findMarksAt(Pos(2, 2));
501
  eq(found.length, 1);
502
  eq(found[0], mAround);
503
}, {value: "aaaa\nbbbb\ncccc\ndddd"});
504
505
testCM("markClearBetween", function(cm) {
506
  cm.setValue("aaa\nbbb\nccc\nddd\n");
507
  cm.markText(Pos(0, 0), Pos(2));
508
  cm.replaceRange("aaa\nbbb\nccc", Pos(0, 0), Pos(2));
509
  eq(cm.findMarksAt(Pos(1, 1)).length, 0);
510
});
511
512
testCM("deleteSpanCollapsedInclusiveLeft", function(cm) {
513
  var from = Pos(1, 0), to = Pos(1, 1);
514
  var m = cm.markText(from, to, {collapsed: true, inclusiveLeft: true});
0 ignored issues
show
Unused Code introduced by
The variable m seems to be never used. Consider removing it.
Loading history...
515
  // Delete collapsed span.
516
  cm.replaceRange("", from, to);
517
}, {value: "abc\nX\ndef"});
518
519
testCM("markTextCSS", function(cm) {
520
  function present() {
521
    var spans = cm.display.lineDiv.getElementsByTagName("span");
522
    for (var i = 0; i < spans.length; i++)
523
      if (spans[i].style.color == "cyan" && span[i].textContent == "cdefg") return true;
0 ignored issues
show
Bug introduced by
The variable span seems to be never declared. If this is a global, consider adding a /** global: span */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
524
  }
525
  var m = cm.markText(Pos(0, 2), Pos(0, 6), {css: "color: cyan"});
526
  m.clear();
527
  is(!present());
528
}, {value: "abcdefgh"});
529
530
testCM("bookmark", function(cm) {
531
  function p(v) { return v && Pos(v[0], v[1]); }
532
  forEach([{a: [1, 0], b: [1, 1], c: "", d: [1, 4]},
533
           {a: [1, 1], b: [1, 1], c: "xx", d: [1, 7]},
534
           {a: [1, 4], b: [1, 5], c: "ab", d: [1, 6]},
535
           {a: [1, 4], b: [1, 6], c: "", d: null},
536
           {a: [1, 5], b: [1, 6], c: "abc", d: [1, 5]},
537
           {a: [1, 6], b: [1, 8], c: "", d: [1, 5]},
538
           {a: [1, 4], b: [1, 4], c: "\n\n", d: [3, 1]},
539
           {bm: [1, 9], a: [1, 1], b: [1, 1], c: "\n", d: [2, 8]}], function(test) {
540
    cm.setValue("1234567890\n1234567890\n1234567890");
541
    var b = cm.setBookmark(p(test.bm) || Pos(1, 5));
542
    cm.replaceRange(test.c, p(test.a), p(test.b));
543
    eqPos(b.find(), p(test.d));
544
  });
545
});
546
547
testCM("bookmarkInsertLeft", function(cm) {
548
  var br = cm.setBookmark(Pos(0, 2), {insertLeft: false});
549
  var bl = cm.setBookmark(Pos(0, 2), {insertLeft: true});
550
  cm.setCursor(Pos(0, 2));
551
  cm.replaceSelection("hi");
552
  eqPos(br.find(), Pos(0, 2));
553
  eqPos(bl.find(), Pos(0, 4));
554
  cm.replaceRange("", Pos(0, 4), Pos(0, 5));
555
  cm.replaceRange("", Pos(0, 2), Pos(0, 4));
556
  cm.replaceRange("", Pos(0, 1), Pos(0, 2));
557
  // Verify that deleting next to bookmarks doesn't kill them
558
  eqPos(br.find(), Pos(0, 1));
559
  eqPos(bl.find(), Pos(0, 1));
560
}, {value: "abcdef"});
561
562
testCM("bookmarkCursor", function(cm) {
563
  var pos01 = cm.cursorCoords(Pos(0, 1)), pos11 = cm.cursorCoords(Pos(1, 1)),
564
      pos20 = cm.cursorCoords(Pos(2, 0)), pos30 = cm.cursorCoords(Pos(3, 0)),
565
      pos41 = cm.cursorCoords(Pos(4, 1));
566
  cm.setBookmark(Pos(0, 1), {widget: document.createTextNode("←"), insertLeft: true});
567
  cm.setBookmark(Pos(2, 0), {widget: document.createTextNode("←"), insertLeft: true});
568
  cm.setBookmark(Pos(1, 1), {widget: document.createTextNode("→")});
569
  cm.setBookmark(Pos(3, 0), {widget: document.createTextNode("→")});
570
  var new01 = cm.cursorCoords(Pos(0, 1)), new11 = cm.cursorCoords(Pos(1, 1)),
571
      new20 = cm.cursorCoords(Pos(2, 0)), new30 = cm.cursorCoords(Pos(3, 0));
572
  near(new01.left, pos01.left, 1);
573
  near(new01.top, pos01.top, 1);
574
  is(new11.left > pos11.left, "at right, middle of line");
575
  near(new11.top == pos11.top, 1);
576
  near(new20.left, pos20.left, 1);
577
  near(new20.top, pos20.top, 1);
578
  is(new30.left > pos30.left, "at right, empty line");
579
  near(new30.top, pos30, 1);
580
  cm.setBookmark(Pos(4, 0), {widget: document.createTextNode("→")});
581
  is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug");
582
}, {value: "foo\nbar\n\n\nx\ny"});
583
584
testCM("multiBookmarkCursor", function(cm) {
585
  if (phantom) return;
586
  var ms = [], m;
587
  function add(insertLeft) {
588
    for (var i = 0; i < 3; ++i) {
589
      var node = document.createElement("span");
590
      node.innerHTML = "X";
591
      ms.push(cm.setBookmark(Pos(0, 1), {widget: node, insertLeft: insertLeft}));
592
    }
593
  }
594
  var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left;
595
  add(true);
596
  near(base1, cm.cursorCoords(Pos(0, 1)).left, 1);
597
  while (m = ms.pop()) m.clear();
598
  add(false);
599
  near(base4, cm.cursorCoords(Pos(0, 1)).left, 1);
600
}, {value: "abcdefg"});
601
602
testCM("getAllMarks", function(cm) {
603
  addDoc(cm, 10, 10);
604
  var m1 = cm.setBookmark(Pos(0, 2));
605
  var m2 = cm.markText(Pos(0, 2), Pos(3, 2));
0 ignored issues
show
Unused Code introduced by
The variable m2 seems to be never used. Consider removing it.
Loading history...
606
  var m3 = cm.markText(Pos(1, 2), Pos(1, 8));
607
  var m4 = cm.markText(Pos(8, 0), Pos(9, 0));
0 ignored issues
show
Unused Code introduced by
The variable m4 seems to be never used. Consider removing it.
Loading history...
608
  eq(cm.getAllMarks().length, 4);
609
  m1.clear();
610
  m3.clear();
611
  eq(cm.getAllMarks().length, 2);
612
});
613
614
testCM("setValueClears", function(cm) {
615
  cm.addLineClass(0, "wrap", "foo");
616
  var mark = cm.markText(Pos(0, 0), Pos(1, 1), {inclusiveLeft: true, inclusiveRight: true});
617
  cm.setValue("foo");
618
  is(!cm.lineInfo(0).wrapClass);
619
  is(!mark.find());
620
}, {value: "a\nb"});
621
622
testCM("bug577", function(cm) {
623
  cm.setValue("a\nb");
624
  cm.clearHistory();
625
  cm.setValue("fooooo");
626
  cm.undo();
627
});
628
629
testCM("scrollSnap", function(cm) {
630
  cm.setSize(100, 100);
631
  addDoc(cm, 200, 200);
632
  cm.setCursor(Pos(100, 180));
633
  var info = cm.getScrollInfo();
634
  is(info.left > 0 && info.top > 0);
635
  cm.setCursor(Pos(0, 0));
636
  info = cm.getScrollInfo();
637
  is(info.left == 0 && info.top == 0, "scrolled clean to top");
0 ignored issues
show
Best Practice introduced by
Comparing info.left to 0 using the == operator is not safe. Consider using === instead.
Loading history...
Best Practice introduced by
Comparing info.top to 0 using the == operator is not safe. Consider using === instead.
Loading history...
638
  cm.setCursor(Pos(100, 180));
639
  cm.setCursor(Pos(199, 0));
640
  info = cm.getScrollInfo();
641
  is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom");
642
});
643
644
testCM("scrollIntoView", function(cm) {
645
  if (phantom) return;
646
  var outer = cm.getWrapperElement().getBoundingClientRect();
647
  function test(line, ch, msg) {
648
    var pos = Pos(line, ch);
649
    cm.scrollIntoView(pos);
650
    var box = cm.charCoords(pos, "window");
651
    is(box.left >= outer.left, msg + " (left)");
652
    is(box.right <= outer.right, msg + " (right)");
653
    is(box.top >= outer.top, msg + " (top)");
654
    is(box.bottom <= outer.bottom, msg + " (bottom)");
655
  }
656
  addDoc(cm, 200, 200);
657
  test(199, 199, "bottom right");
658
  test(0, 0, "top left");
659
  test(100, 100, "center");
660
  test(199, 0, "bottom left");
661
  test(0, 199, "top right");
662
  test(100, 100, "center again");
663
});
664
665
testCM("scrollBackAndForth", function(cm) {
666
  addDoc(cm, 1, 200);
667
  cm.operation(function() {
668
    cm.scrollIntoView(Pos(199, 0));
669
    cm.scrollIntoView(Pos(4, 0));
670
  });
671
  is(cm.getScrollInfo().top > 0);
672
});
673
674
testCM("selectAllNoScroll", function(cm) {
675
  addDoc(cm, 1, 200);
676
  cm.execCommand("selectAll");
677
  eq(cm.getScrollInfo().top, 0);
678
  cm.setCursor(199);
679
  cm.execCommand("selectAll");
680
  is(cm.getScrollInfo().top > 0);
681
});
682
683
testCM("selectionPos", function(cm) {
684
  if (phantom || cm.getOption("inputStyle") != "textarea") return;
685
  cm.setSize(100, 100);
686
  addDoc(cm, 200, 100);
687
  cm.setSelection(Pos(1, 100), Pos(98, 100));
688
  var lineWidth = cm.charCoords(Pos(0, 200), "local").left;
689
  var lineHeight = (cm.charCoords(Pos(99)).top - cm.charCoords(Pos(0)).top) / 100;
690
  cm.scrollTo(0, 0);
691
  var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected");
692
  var outer = cm.getWrapperElement().getBoundingClientRect();
693
  var sawMiddle, sawTop, sawBottom;
694
  for (var i = 0, e = selElt.length; i < e; ++i) {
695
    var box = selElt[i].getBoundingClientRect();
696
    var atLeft = box.left - outer.left < 30;
697
    var width = box.right - box.left;
698
    var atRight = box.right - outer.left > .8 * lineWidth;
699
    if (atLeft && atRight) {
700
      sawMiddle = true;
701
      is(box.bottom - box.top > 90 * lineHeight, "middle high");
702
      is(width > .9 * lineWidth, "middle wide");
703
    } else {
704
      is(width > .4 * lineWidth, "top/bot wide enough");
705
      is(width < .6 * lineWidth, "top/bot slim enough");
706
      if (atLeft) {
707
        sawBottom = true;
708
        is(box.top - outer.top > 96 * lineHeight, "bot below");
709
      } else if (atRight) {
710
        sawTop = true;
711
        is(box.top - outer.top < 2.1 * lineHeight, "top above");
712
      }
713
    }
714
  }
715
  is(sawTop && sawBottom && sawMiddle, "all parts");
716
}, null);
717
718
testCM("restoreHistory", function(cm) {
719
  cm.setValue("abc\ndef");
720
  cm.replaceRange("hello", Pos(1, 0), Pos(1));
721
  cm.replaceRange("goop", Pos(0, 0), Pos(0));
722
  cm.undo();
723
  var storedVal = cm.getValue(), storedHist = cm.getHistory();
724
  if (window.JSON) storedHist = JSON.parse(JSON.stringify(storedHist));
725
  eq(storedVal, "abc\nhello");
726
  cm.setValue("");
727
  cm.clearHistory();
728
  eq(cm.historySize().undo, 0);
729
  cm.setValue(storedVal);
730
  cm.setHistory(storedHist);
731
  cm.redo();
732
  eq(cm.getValue(), "goop\nhello");
733
  cm.undo(); cm.undo();
734
  eq(cm.getValue(), "abc\ndef");
735
});
736
737
testCM("doubleScrollbar", function(cm) {
738
  var dummy = document.body.appendChild(document.createElement("p"));
739
  dummy.style.cssText = "height: 50px; overflow: scroll; width: 50px";
740
  var scrollbarWidth = dummy.offsetWidth + 1 - dummy.clientWidth;
741
  document.body.removeChild(dummy);
742
  if (scrollbarWidth < 2) return;
743
  cm.setSize(null, 100);
744
  addDoc(cm, 1, 300);
745
  var wrap = cm.getWrapperElement();
746
  is(wrap.offsetWidth - byClassName(wrap, "CodeMirror-lines")[0].offsetWidth <= scrollbarWidth * 1.5);
747
});
748
749
testCM("weirdLinebreaks", function(cm) {
750
  cm.setValue("foo\nbar\rbaz\r\nquux\n\rplop");
751
  is(cm.getValue(), "foo\nbar\nbaz\nquux\n\nplop");
752
  is(cm.lineCount(), 6);
753
  cm.setValue("\n\n");
754
  is(cm.lineCount(), 3);
755
});
756
757
testCM("setSize", function(cm) {
758
  cm.setSize(100, 100);
759
  var wrap = cm.getWrapperElement();
760
  is(wrap.offsetWidth, 100);
761
  is(wrap.offsetHeight, 100);
762
  cm.setSize("100%", "3em");
763
  is(wrap.style.width, "100%");
764
  is(wrap.style.height, "3em");
765
  cm.setSize(null, 40);
766
  is(wrap.style.width, "100%");
767
  is(wrap.style.height, "40px");
768
});
769
770
function foldLines(cm, start, end, autoClear) {
771
  return cm.markText(Pos(start, 0), Pos(end - 1), {
772
    inclusiveLeft: true,
773
    inclusiveRight: true,
774
    collapsed: true,
775
    clearOnEnter: autoClear
776
  });
777
}
778
779
testCM("collapsedLines", function(cm) {
780
  addDoc(cm, 4, 10);
781
  var range = foldLines(cm, 4, 5), cleared = 0;
782
  CodeMirror.on(range, "clear", function() {cleared++;});
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
783
  cm.setCursor(Pos(3, 0));
784
  CodeMirror.commands.goLineDown(cm);
785
  eqPos(cm.getCursor(), Pos(5, 0));
786
  cm.replaceRange("abcdefg", Pos(3, 0), Pos(3));
787
  cm.setCursor(Pos(3, 6));
788
  CodeMirror.commands.goLineDown(cm);
789
  eqPos(cm.getCursor(), Pos(5, 4));
790
  cm.replaceRange("ab", Pos(3, 0), Pos(3));
791
  cm.setCursor(Pos(3, 2));
792
  CodeMirror.commands.goLineDown(cm);
793
  eqPos(cm.getCursor(), Pos(5, 2));
794
  cm.operation(function() {range.clear(); range.clear();});
795
  eq(cleared, 1);
796
});
797
798
testCM("collapsedRangeCoordsChar", function(cm) {
799
  var pos_1_3 = cm.charCoords(Pos(1, 3));
800
  pos_1_3.left += 2; pos_1_3.top += 2;
801
  var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true};
802
  var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts);
803
  eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
804
  m1.clear();
805
  var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true});
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable m1 already seems to be declared on line 802. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
806
  var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true});
807
  eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
808
  m1.clear(); m2.clear();
809
  var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable m1 already seems to be declared on line 802. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
Unused Code introduced by
The assignment to variable m1 seems to be never used. Consider removing it.
Loading history...
810
  eqPos(cm.coordsChar(pos_1_3), Pos(3, 3));
811
}, {value: "123456\nabcdef\nghijkl\nmnopqr\n"});
812
813
testCM("collapsedRangeBetweenLinesSelected", function(cm) {
814
  if (cm.getOption("inputStyle") != "textarea") return;
815
  var widget = document.createElement("span");
816
  widget.textContent = "\u2194";
817
  cm.markText(Pos(0, 3), Pos(1, 0), {replacedWith: widget});
818
  cm.setSelection(Pos(0, 3), Pos(1, 0));
819
  var selElts = byClassName(cm.getWrapperElement(), "CodeMirror-selected");
820
  for (var i = 0, w = 0; i < selElts.length; i++)
821
    w += selElts[i].offsetWidth;
822
  is(w > 0);
823
}, {value: "one\ntwo"});
824
825
testCM("randomCollapsedRanges", function(cm) {
826
  addDoc(cm, 20, 500);
827
  cm.operation(function() {
828
    for (var i = 0; i < 200; i++) {
829
      var start = Pos(Math.floor(Math.random() * 500), Math.floor(Math.random() * 20));
830
      if (i % 4)
831
        try { cm.markText(start, Pos(start.line + 2, 1), {collapsed: true}); }
832
        catch(e) { if (!/overlapping/.test(String(e))) throw e; }
833
      else
834
        cm.markText(start, Pos(start.line, start.ch + 4), {"className": "foo"});
835
    }
836
  });
837
});
838
839
testCM("hiddenLinesAutoUnfold", function(cm) {
840
  var range = foldLines(cm, 1, 3, true), cleared = 0;
841
  CodeMirror.on(range, "clear", function() {cleared++;});
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
842
  cm.setCursor(Pos(3, 0));
843
  eq(cleared, 0);
844
  cm.execCommand("goCharLeft");
845
  eq(cleared, 1);
846
  range = foldLines(cm, 1, 3, true);
847
  CodeMirror.on(range, "clear", function() {cleared++;});
848
  eqPos(cm.getCursor(), Pos(3, 0));
849
  cm.setCursor(Pos(0, 3));
850
  cm.execCommand("goCharRight");
851
  eq(cleared, 2);
852
}, {value: "abc\ndef\nghi\njkl"});
853
854
testCM("hiddenLinesSelectAll", function(cm) {  // Issue #484
855
  addDoc(cm, 4, 20);
856
  foldLines(cm, 0, 10);
857
  foldLines(cm, 11, 20);
858
  CodeMirror.commands.selectAll(cm);
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
859
  eqPos(cm.getCursor(true), Pos(10, 0));
860
  eqPos(cm.getCursor(false), Pos(10, 4));
861
});
862
863
864
testCM("everythingFolded", function(cm) {
865
  addDoc(cm, 2, 2);
866
  function enterPress() {
867
    cm.triggerOnKeyDown({type: "keydown", keyCode: 13, preventDefault: function(){}, stopPropagation: function(){}});
868
  }
869
  var fold = foldLines(cm, 0, 2);
870
  enterPress();
871
  eq(cm.getValue(), "xx\nxx");
872
  fold.clear();
873
  fold = foldLines(cm, 0, 2, true);
874
  eq(fold.find(), null);
875
  enterPress();
876
  eq(cm.getValue(), "\nxx\nxx");
877
});
878
879
testCM("structuredFold", function(cm) {
880
  if (phantom) return;
881
  addDoc(cm, 4, 8);
882
  var range = cm.markText(Pos(1, 2), Pos(6, 2), {
0 ignored issues
show
Unused Code introduced by
The assignment to variable range seems to be never used. Consider removing it.
Loading history...
883
    replacedWith: document.createTextNode("Q")
884
  });
885
  cm.setCursor(0, 3);
886
  CodeMirror.commands.goLineDown(cm);
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
887
  eqPos(cm.getCursor(), Pos(6, 2));
888
  CodeMirror.commands.goCharLeft(cm);
889
  eqPos(cm.getCursor(), Pos(1, 2));
890
  CodeMirror.commands.delCharAfter(cm);
891
  eq(cm.getValue(), "xxxx\nxxxx\nxxxx");
892
  addDoc(cm, 4, 8);
893
  range = cm.markText(Pos(1, 2), Pos(6, 2), {
894
    replacedWith: document.createTextNode("M"),
895
    clearOnEnter: true
896
  });
897
  var cleared = 0;
898
  CodeMirror.on(range, "clear", function(){++cleared;});
899
  cm.setCursor(0, 3);
900
  CodeMirror.commands.goLineDown(cm);
901
  eqPos(cm.getCursor(), Pos(6, 2));
902
  CodeMirror.commands.goCharLeft(cm);
903
  eqPos(cm.getCursor(), Pos(6, 1));
904
  eq(cleared, 1);
905
  range.clear();
906
  eq(cleared, 1);
907
  range = cm.markText(Pos(1, 2), Pos(6, 2), {
908
    replacedWith: document.createTextNode("Q"),
909
    clearOnEnter: true
910
  });
911
  range.clear();
912
  cm.setCursor(1, 2);
913
  CodeMirror.commands.goCharRight(cm);
914
  eqPos(cm.getCursor(), Pos(1, 3));
915
  range = cm.markText(Pos(2, 0), Pos(4, 4), {
916
    replacedWith: document.createTextNode("M")
917
  });
918
  cm.setCursor(1, 0);
919
  CodeMirror.commands.goLineDown(cm);
920
  eqPos(cm.getCursor(), Pos(2, 0));
921
}, null);
922
923
testCM("nestedFold", function(cm) {
924
  addDoc(cm, 10, 3);
925
  function fold(ll, cl, lr, cr) {
926
    return cm.markText(Pos(ll, cl), Pos(lr, cr), {collapsed: true});
927
  }
928
  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);
0 ignored issues
show
Unused Code introduced by
The variable inner1 seems to be never used. Consider removing it.
Loading history...
929
  cm.setCursor(0, 1);
930
  CodeMirror.commands.goCharRight(cm);
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
931
  eqPos(cm.getCursor(), Pos(2, 3));
932
  inner0.clear();
933
  CodeMirror.commands.goCharLeft(cm);
934
  eqPos(cm.getCursor(), Pos(0, 1));
935
  outer.clear();
936
  CodeMirror.commands.goCharRight(cm);
937
  eqPos(cm.getCursor(), Pos(0, 2));
938
  CodeMirror.commands.goCharRight(cm);
939
  eqPos(cm.getCursor(), Pos(1, 8));
940
  inner2.clear();
941
  CodeMirror.commands.goCharLeft(cm);
942
  eqPos(cm.getCursor(), Pos(1, 7));
943
  cm.setCursor(0, 5);
944
  CodeMirror.commands.goCharRight(cm);
945
  eqPos(cm.getCursor(), Pos(0, 6));
946
  CodeMirror.commands.goCharRight(cm);
947
  eqPos(cm.getCursor(), Pos(1, 3));
948
});
949
950
testCM("badNestedFold", function(cm) {
951
  addDoc(cm, 4, 4);
952
  cm.markText(Pos(0, 2), Pos(3, 2), {collapsed: true});
953
  var caught;
954
  try {cm.markText(Pos(0, 1), Pos(0, 3), {collapsed: true});}
955
  catch(e) {caught = e;}
956
  is(caught instanceof Error, "no error");
0 ignored issues
show
Bug introduced by
The variable caught seems to not be initialized for all possible execution paths.
Loading history...
957
  is(/overlap/i.test(caught.message), "wrong error");
958
});
959
960
testCM("nestedFoldOnSide", function(cm) {
961
  var m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true, inclusiveRight: true});
962
  var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true});
0 ignored issues
show
Unused Code introduced by
The variable m2 seems to be never used. Consider removing it.
Loading history...
963
  cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}).clear();
964
  try { cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true, inclusiveLeft: true}); }
965
  catch(e) { var caught = e; }
966
  is(caught && /overlap/i.test(caught.message));
967
  var m3 = cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true});
0 ignored issues
show
Unused Code introduced by
The variable m3 seems to be never used. Consider removing it.
Loading history...
968
  var m4 = cm.markText(Pos(2, 0), Pos(2, 1), {collapse: true, inclusiveRight: true});
969
  m1.clear(); m4.clear();
970
  m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true});
0 ignored issues
show
Unused Code introduced by
The assignment to variable m1 seems to be never used. Consider removing it.
Loading history...
971
  cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}).clear();
972
  try { cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true, inclusiveRight: true}); }
973
  catch(e) { var caught = e; }
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable caught already seems to be declared on line 965. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
974
  is(caught && /overlap/i.test(caught.message));
975
}, {value: "ab\ncd\ef"});
976
977
testCM("editInFold", function(cm) {
978
  addDoc(cm, 4, 6);
979
  var m = cm.markText(Pos(1, 2), Pos(3, 2), {collapsed: true});
0 ignored issues
show
Unused Code introduced by
The variable m seems to be never used. Consider removing it.
Loading history...
980
  cm.replaceRange("", Pos(0, 0), Pos(1, 3));
981
  cm.replaceRange("", Pos(2, 1), Pos(3, 3));
982
  cm.replaceRange("a\nb\nc\nd", Pos(0, 1), Pos(1, 0));
983
  cm.cursorCoords(Pos(0, 0));
984
});
985
986
testCM("wrappingInlineWidget", function(cm) {
987
  cm.setSize("11em");
988
  var w = document.createElement("span");
989
  w.style.color = "red";
990
  w.innerHTML = "one two three four";
991
  cm.markText(Pos(0, 6), Pos(0, 9), {replacedWith: w});
992
  var cur0 = cm.cursorCoords(Pos(0, 0)), cur1 = cm.cursorCoords(Pos(0, 10));
993
  is(cur0.top < cur1.top);
994
  is(cur0.bottom < cur1.bottom);
995
  var curL = cm.cursorCoords(Pos(0, 6)), curR = cm.cursorCoords(Pos(0, 9));
996
  eq(curL.top, cur0.top);
997
  eq(curL.bottom, cur0.bottom);
998
  eq(curR.top, cur1.top);
999
  eq(curR.bottom, cur1.bottom);
1000
  cm.replaceRange("", Pos(0, 9), Pos(0));
1001
  curR = cm.cursorCoords(Pos(0, 9));
1002
  if (phantom) return;
1003
  eq(curR.top, cur1.top);
1004
  eq(curR.bottom, cur1.bottom);
1005
}, {value: "1 2 3 xxx 4", lineWrapping: true});
1006
1007
testCM("showEmptyWidgetSpan", function(cm) {
1008
  var marker = cm.markText(Pos(0, 2), Pos(0, 2), {
0 ignored issues
show
Unused Code introduced by
The variable marker seems to be never used. Consider removing it.
Loading history...
1009
    clearWhenEmpty: false,
1010
    replacedWith: document.createTextNode("X")
1011
  });
1012
  eq(cm.display.view[0].text.textContent, "abXc");
1013
}, {value: "abc"});
1014
1015
testCM("changedInlineWidget", function(cm) {
1016
  cm.setSize("10em");
1017
  var w = document.createElement("span");
1018
  w.innerHTML = "x";
1019
  var m = cm.markText(Pos(0, 4), Pos(0, 5), {replacedWith: w});
1020
  w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed";
1021
  m.changed();
1022
  var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0];
1023
  is(hScroll.scrollWidth > hScroll.clientWidth);
1024
}, {value: "hello there"});
1025
1026
testCM("changedBookmark", function(cm) {
1027
  cm.setSize("10em");
1028
  var w = document.createElement("span");
1029
  w.innerHTML = "x";
1030
  var m = cm.setBookmark(Pos(0, 4), {widget: w});
1031
  w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed";
1032
  m.changed();
1033
  var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0];
1034
  is(hScroll.scrollWidth > hScroll.clientWidth);
1035
}, {value: "abcdefg"});
1036
1037
testCM("inlineWidget", function(cm) {
1038
  var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")});
1039
  cm.setCursor(0, 2);
1040
  CodeMirror.commands.goLineDown(cm);
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1041
  eqPos(cm.getCursor(), Pos(1, 4));
1042
  cm.setCursor(0, 2);
1043
  cm.replaceSelection("hi");
1044
  eqPos(w.find(), Pos(0, 2));
1045
  cm.setCursor(0, 1);
1046
  cm.replaceSelection("ay");
1047
  eqPos(w.find(), Pos(0, 4));
1048
  eq(cm.getLine(0), "uayuhiuu");
1049
}, {value: "uuuu\nuuuuuu"});
1050
1051
testCM("wrappingAndResizing", function(cm) {
1052
  cm.setSize(null, "auto");
1053
  cm.setOption("lineWrapping", true);
1054
  var wrap = cm.getWrapperElement(), h0 = wrap.offsetHeight;
1055
  var doc = "xxx xxx xxx xxx xxx";
1056
  cm.setValue(doc);
1057
  for (var step = 10, w = cm.charCoords(Pos(0, 18), "div").right;; w += step) {
1058
    cm.setSize(w);
1059
    if (wrap.offsetHeight <= h0 * (opera_lt10 ? 1.2 : 1.5)) {
1060
      if (step == 10) { w -= 10; step = 1; }
1061
      else break;
1062
    }
1063
  }
1064
  // Ensure that putting the cursor at the end of the maximally long
1065
  // line doesn't cause wrapping to happen.
1066
  cm.setCursor(Pos(0, doc.length));
1067
  eq(wrap.offsetHeight, h0);
1068
  cm.replaceSelection("x");
1069
  is(wrap.offsetHeight > h0, "wrapping happens");
1070
  // Now add a max-height and, in a document consisting of
1071
  // almost-wrapped lines, go over it so that a scrollbar appears.
1072
  cm.setValue(doc + "\n" + doc + "\n");
1073
  cm.getScrollerElement().style.maxHeight = "100px";
1074
  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));
1075
  forEach([Pos(0, doc.length), Pos(0, doc.length - 1),
1076
           Pos(0, 0), Pos(1, doc.length), Pos(1, doc.length - 1)],
1077
          function(pos) {
1078
    var coords = cm.charCoords(pos);
1079
    eqPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5}));
1080
  });
1081
}, null, ie_lt8);
1082
1083
testCM("measureEndOfLine", function(cm) {
1084
  cm.setSize(null, "auto");
1085
  var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild;
1086
  var lh = inner.offsetHeight;
1087
  for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) {
1088
    cm.setSize(w);
1089
    if (inner.offsetHeight < 2.5 * lh) {
1090
      if (step == 10) { w -= 10; step = 1; }
1091
      else break;
1092
    }
1093
  }
1094
  cm.setValue(cm.getValue() + "\n\n");
1095
  var endPos = cm.charCoords(Pos(0, 18), "local");
1096
  is(endPos.top > lh * .8, "not at top");
1097
  is(endPos.left > w - 20, "not at right");
1098
  endPos = cm.charCoords(Pos(0, 18));
1099
  eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18));
1100
}, {mode: "text/html", value: "<!-- foo barrr -->", lineWrapping: true}, ie_lt8 || opera_lt10);
1101
1102
testCM("scrollVerticallyAndHorizontally", function(cm) {
1103
  if (cm.getOption("inputStyle") != "textarea") return;
1104
  cm.setSize(100, 100);
1105
  addDoc(cm, 40, 40);
1106
  cm.setCursor(39);
1107
  var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-vscrollbar")[0];
1108
  is(bar.offsetHeight < wrap.offsetHeight, "vertical scrollbar limited by horizontal one");
1109
  var cursorBox = byClassName(wrap, "CodeMirror-cursor")[0].getBoundingClientRect();
1110
  var editorBox = wrap.getBoundingClientRect();
1111
  is(cursorBox.bottom < editorBox.top + cm.getScrollerElement().clientHeight,
1112
     "bottom line visible");
1113
}, {lineNumbers: true});
1114
1115
testCM("moveVstuck", function(cm) {
1116
  var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight;
1117
  var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n";
1118
  cm.setValue(val);
1119
  for (var w = cm.charCoords(Pos(0, 26), "div").right * 2.8;; w += 5) {
1120
    cm.setSize(w);
1121
    if (lines.offsetHeight <= 3.5 * h0) break;
1122
  }
1123
  cm.setCursor(Pos(0, val.length - 1));
1124
  cm.moveV(-1, "line");
1125
  eqPos(cm.getCursor(), Pos(0, 26));
1126
}, {lineWrapping: true}, ie_lt8 || opera_lt10);
1127
1128
testCM("collapseOnMove", function(cm) {
1129
  cm.setSelection(Pos(0, 1), Pos(2, 4));
1130
  cm.execCommand("goLineUp");
1131
  is(!cm.somethingSelected());
1132
  eqPos(cm.getCursor(), Pos(0, 1));
1133
  cm.setSelection(Pos(0, 1), Pos(2, 4));
1134
  cm.execCommand("goPageDown");
1135
  is(!cm.somethingSelected());
1136
  eqPos(cm.getCursor(), Pos(2, 4));
1137
  cm.execCommand("goLineUp");
1138
  cm.execCommand("goLineUp");
1139
  eqPos(cm.getCursor(), Pos(0, 4));
1140
  cm.setSelection(Pos(0, 1), Pos(2, 4));
1141
  cm.execCommand("goCharLeft");
1142
  is(!cm.somethingSelected());
1143
  eqPos(cm.getCursor(), Pos(0, 1));
1144
}, {value: "aaaaa\nb\nccccc"});
1145
1146
testCM("clickTab", function(cm) {
1147
  var p0 = cm.charCoords(Pos(0, 0));
1148
  eqPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0));
1149
  eqPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), Pos(0, 1));
1150
}, {value: "\t\n\n", lineWrapping: true, tabSize: 8});
1151
1152
testCM("verticalScroll", function(cm) {
1153
  cm.setSize(100, 200);
1154
  cm.setValue("foo\nbar\nbaz\n");
1155
  var sc = cm.getScrollerElement(), baseWidth = sc.scrollWidth;
1156
  cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0));
1157
  is(sc.scrollWidth > baseWidth, "scrollbar present");
1158
  cm.replaceRange("foo", Pos(0, 0), Pos(0));
1159
  if (!phantom) eq(sc.scrollWidth, baseWidth, "scrollbar gone");
1160
  cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0));
1161
  cm.replaceRange("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh", Pos(1, 0), Pos(1));
1162
  is(sc.scrollWidth > baseWidth, "present again");
1163
  var curWidth = sc.scrollWidth;
1164
  cm.replaceRange("foo", Pos(0, 0), Pos(0));
1165
  is(sc.scrollWidth < curWidth, "scrollbar smaller");
1166
  is(sc.scrollWidth > baseWidth, "but still present");
1167
});
1168
1169
testCM("extraKeys", function(cm) {
1170
  var outcome;
1171
  function fakeKey(expected, code, props) {
1172
    if (typeof code == "string") code = code.charCodeAt(0);
1173
    var e = {type: "keydown", keyCode: code, preventDefault: function(){}, stopPropagation: function(){}};
1174
    if (props) for (var n in props) e[n] = props[n];
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
1175
    outcome = null;
1176
    cm.triggerOnKeyDown(e);
1177
    eq(outcome, expected);
1178
  }
1179
  CodeMirror.commands.testCommand = function() {outcome = "tc";};
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1180
  CodeMirror.commands.goTestCommand = function() {outcome = "gtc";};
1181
  cm.setOption("extraKeys", {"Shift-X": function() {outcome = "sx";},
1182
                             "X": function() {outcome = "x";},
1183
                             "Ctrl-Alt-U": function() {outcome = "cau";},
1184
                             "End": "testCommand",
1185
                             "Home": "goTestCommand",
1186
                             "Tab": false});
1187
  fakeKey(null, "U");
1188
  fakeKey("cau", "U", {ctrlKey: true, altKey: true});
1189
  fakeKey(null, "U", {shiftKey: true, ctrlKey: true, altKey: true});
1190
  fakeKey("x", "X");
1191
  fakeKey("sx", "X", {shiftKey: true});
1192
  fakeKey("tc", 35);
1193
  fakeKey(null, 35, {shiftKey: true});
1194
  fakeKey("gtc", 36);
1195
  fakeKey("gtc", 36, {shiftKey: true});
1196
  fakeKey(null, 9);
1197
}, null, window.opera && mac);
1198
1199
testCM("wordMovementCommands", function(cm) {
1200
  cm.execCommand("goWordLeft");
1201
  eqPos(cm.getCursor(), Pos(0, 0));
1202
  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
1203
  eqPos(cm.getCursor(), Pos(0, 7));
1204
  cm.execCommand("goWordLeft");
1205
  eqPos(cm.getCursor(), Pos(0, 5));
1206
  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
1207
  eqPos(cm.getCursor(), Pos(0, 12));
1208
  cm.execCommand("goWordLeft");
1209
  eqPos(cm.getCursor(), Pos(0, 9));
1210
  cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
1211
  eqPos(cm.getCursor(), Pos(0, 24));
1212
  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
1213
  eqPos(cm.getCursor(), Pos(1, 9));
1214
  cm.execCommand("goWordRight");
1215
  eqPos(cm.getCursor(), Pos(1, 13));
1216
  cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
1217
  eqPos(cm.getCursor(), Pos(2, 0));
1218
}, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"});
1219
1220
testCM("groupMovementCommands", function(cm) {
1221
  cm.execCommand("goGroupLeft");
1222
  eqPos(cm.getCursor(), Pos(0, 0));
1223
  cm.execCommand("goGroupRight");
1224
  eqPos(cm.getCursor(), Pos(0, 4));
1225
  cm.execCommand("goGroupRight");
1226
  eqPos(cm.getCursor(), Pos(0, 7));
1227
  cm.execCommand("goGroupRight");
1228
  eqPos(cm.getCursor(), Pos(0, 10));
1229
  cm.execCommand("goGroupLeft");
1230
  eqPos(cm.getCursor(), Pos(0, 7));
1231
  cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight");
1232
  eqPos(cm.getCursor(), Pos(0, 15));
1233
  cm.setCursor(Pos(0, 17));
1234
  cm.execCommand("goGroupLeft");
1235
  eqPos(cm.getCursor(), Pos(0, 16));
1236
  cm.execCommand("goGroupLeft");
1237
  eqPos(cm.getCursor(), Pos(0, 14));
1238
  cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight");
1239
  eqPos(cm.getCursor(), Pos(0, 20));
1240
  cm.execCommand("goGroupRight");
1241
  eqPos(cm.getCursor(), Pos(1, 0));
1242
  cm.execCommand("goGroupRight");
1243
  eqPos(cm.getCursor(), Pos(1, 2));
1244
  cm.execCommand("goGroupRight");
1245
  eqPos(cm.getCursor(), Pos(1, 5));
1246
  cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft");
1247
  eqPos(cm.getCursor(), Pos(1, 0));
1248
  cm.execCommand("goGroupLeft");
1249
  eqPos(cm.getCursor(), Pos(0, 20));
1250
  cm.execCommand("goGroupLeft");
1251
  eqPos(cm.getCursor(), Pos(0, 16));
1252
}, {value: "booo ba---quux. ffff\n  abc d"});
1253
1254
testCM("groupsAndWhitespace", function(cm) {
1255
  var positions = [Pos(0, 0), Pos(0, 2), Pos(0, 5), Pos(0, 9), Pos(0, 11),
1256
                   Pos(1, 0), Pos(1, 2), Pos(1, 5)];
1257
  for (var i = 1; i < positions.length; i++) {
1258
    cm.execCommand("goGroupRight");
1259
    eqPos(cm.getCursor(), positions[i]);
1260
  }
1261
  for (var i = positions.length - 2; i >= 0; i--) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 1257. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1262
    cm.execCommand("goGroupLeft");
1263
    eqPos(cm.getCursor(), i == 2 ? Pos(0, 6) : positions[i]);
1264
  }
1265
}, {value: "  foo +++  \n  bar"});
1266
1267
testCM("charMovementCommands", function(cm) {
1268
  cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft");
1269
  eqPos(cm.getCursor(), Pos(0, 0));
1270
  cm.execCommand("goCharRight"); cm.execCommand("goCharRight");
1271
  eqPos(cm.getCursor(), Pos(0, 2));
1272
  cm.setCursor(Pos(1, 0));
1273
  cm.execCommand("goColumnLeft");
1274
  eqPos(cm.getCursor(), Pos(1, 0));
1275
  cm.execCommand("goCharLeft");
1276
  eqPos(cm.getCursor(), Pos(0, 5));
1277
  cm.execCommand("goColumnRight");
1278
  eqPos(cm.getCursor(), Pos(0, 5));
1279
  cm.execCommand("goCharRight");
1280
  eqPos(cm.getCursor(), Pos(1, 0));
1281
  cm.execCommand("goLineEnd");
1282
  eqPos(cm.getCursor(), Pos(1, 5));
1283
  cm.execCommand("goLineStartSmart");
1284
  eqPos(cm.getCursor(), Pos(1, 1));
1285
  cm.execCommand("goLineStartSmart");
1286
  eqPos(cm.getCursor(), Pos(1, 0));
1287
  cm.setCursor(Pos(2, 0));
1288
  cm.execCommand("goCharRight"); cm.execCommand("goColumnRight");
1289
  eqPos(cm.getCursor(), Pos(2, 0));
1290
}, {value: "line1\n ine2\n"});
1291
1292
testCM("verticalMovementCommands", function(cm) {
1293
  cm.execCommand("goLineUp");
1294
  eqPos(cm.getCursor(), Pos(0, 0));
1295
  cm.execCommand("goLineDown");
1296
  if (!phantom) // This fails in PhantomJS, though not in a real Webkit
1297
    eqPos(cm.getCursor(), Pos(1, 0));
1298
  cm.setCursor(Pos(1, 12));
1299
  cm.execCommand("goLineDown");
1300
  eqPos(cm.getCursor(), Pos(2, 5));
1301
  cm.execCommand("goLineDown");
1302
  eqPos(cm.getCursor(), Pos(3, 0));
1303
  cm.execCommand("goLineUp");
1304
  eqPos(cm.getCursor(), Pos(2, 5));
1305
  cm.execCommand("goLineUp");
1306
  eqPos(cm.getCursor(), Pos(1, 12));
1307
  cm.execCommand("goPageDown");
1308
  eqPos(cm.getCursor(), Pos(5, 0));
1309
  cm.execCommand("goPageDown"); cm.execCommand("goLineDown");
1310
  eqPos(cm.getCursor(), Pos(5, 0));
1311
  cm.execCommand("goPageUp");
1312
  eqPos(cm.getCursor(), Pos(0, 0));
1313
}, {value: "line1\nlong long line2\nline3\n\nline5\n"});
1314
1315
testCM("verticalMovementCommandsWrapping", function(cm) {
1316
  cm.setSize(120);
1317
  cm.setCursor(Pos(0, 5));
1318
  cm.execCommand("goLineDown");
1319
  eq(cm.getCursor().line, 0);
1320
  is(cm.getCursor().ch > 5, "moved beyond wrap");
1321
  for (var i = 0; ; ++i) {
1322
    is(i < 20, "no endless loop");
1323
    cm.execCommand("goLineDown");
1324
    var cur = cm.getCursor();
1325
    if (cur.line == 1) eq(cur.ch, 5);
0 ignored issues
show
Best Practice introduced by
Comparing cur.line to 1 using the == operator is not safe. Consider using === instead.
Loading history...
1326
    if (cur.line == 2) { eq(cur.ch, 1); break; }
1327
  }
1328
}, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk",
1329
    lineWrapping: true});
1330
1331
testCM("rtlMovement", function(cm) {
1332
  if (cm.getOption("inputStyle") != "textarea") return;
1333
  forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج",
1334
           "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق",
1335
           "<img src=\"/בדיקה3.jpg\">", "يتم السحب في 05 فبراير 2014"], function(line) {
1336
    var inv = line.charCodeAt(0) > 128;
1337
    cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart");
1338
    var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0];
1339
    var cursor = cursors.firstChild;
1340
    var prevX = cursor.offsetLeft, prevY = cursor.offsetTop;
1341
    for (var i = 0; i <= line.length; ++i) {
1342
      cm.execCommand("goCharRight");
1343
      cursor = cursors.firstChild;
1344
      if (i == line.length) is(cursor.offsetTop > prevY, "next line");
1345
      else is(cursor.offsetLeft > prevX, "moved right");
1346
      prevX = cursor.offsetLeft; prevY = cursor.offsetTop;
1347
    }
1348
    cm.setCursor(0, 0); cm.execCommand(inv ? "goLineStart" : "goLineEnd");
1349
    prevX = cursors.firstChild.offsetLeft;
1350
    for (var i = 0; i < line.length; ++i) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 1341. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1351
      cm.execCommand("goCharLeft");
1352
      cursor = cursors.firstChild;
1353
      is(cursor.offsetLeft < prevX, "moved left");
1354
      prevX = cursor.offsetLeft;
1355
    }
1356
  });
1357
}, null, ie_lt9);
1358
1359
// Verify that updating a line clears its bidi ordering
1360
testCM("bidiUpdate", function(cm) {
1361
  cm.setCursor(Pos(0, 2));
1362
  cm.replaceSelection("خحج", "start");
1363
  cm.execCommand("goCharRight");
1364
  eqPos(cm.getCursor(), Pos(0, 4));
1365
}, {value: "abcd\n"});
1366
1367
testCM("movebyTextUnit", function(cm) {
1368
  cm.setValue("בְּרֵאשִ\nééé́\n");
1369
  cm.execCommand("goLineEnd");
1370
  for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight");
1371
  eqPos(cm.getCursor(), Pos(0, 0));
1372
  cm.execCommand("goCharRight");
1373
  eqPos(cm.getCursor(), Pos(1, 0));
1374
  cm.execCommand("goCharRight");
1375
  cm.execCommand("goCharRight");
1376
  eqPos(cm.getCursor(), Pos(1, 4));
1377
  cm.execCommand("goCharRight");
1378
  eqPos(cm.getCursor(), Pos(1, 7));
1379
});
1380
1381
testCM("lineChangeEvents", function(cm) {
1382
  addDoc(cm, 3, 5);
1383
  var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"];
1384
  for (var i = 0; i < 5; ++i) {
1385
    CodeMirror.on(cm.getLineHandle(i), "delete", function(i) {
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1386
      return function() {log.push("del " + i);};
1387
    }(i));
1388
    CodeMirror.on(cm.getLineHandle(i), "change", function(i) {
1389
      return function() {log.push("ch " + i);};
1390
    }(i));
1391
  }
1392
  cm.replaceRange("x", Pos(0, 1));
1393
  cm.replaceRange("xy", Pos(1, 1), Pos(2));
1394
  cm.replaceRange("foo\nbar", Pos(0, 1));
1395
  cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount()));
1396
  eq(log.length, want.length, "same length");
1397
  for (var i = 0; i < log.length; ++i)
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 1384. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1398
    eq(log[i], want[i]);
1399
});
1400
1401
testCM("scrollEntirelyToRight", function(cm) {
1402
  if (phantom || cm.getOption("inputStyle") != "textarea") return;
1403
  addDoc(cm, 500, 2);
1404
  cm.setCursor(Pos(0, 500));
1405
  var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0];
1406
  is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left);
1407
});
1408
1409
testCM("lineWidgets", function(cm) {
1410
  addDoc(cm, 500, 3);
1411
  var last = cm.charCoords(Pos(2, 0));
1412
  var node = document.createElement("div");
1413
  node.innerHTML = "hi";
1414
  var widget = cm.addLineWidget(1, node);
0 ignored issues
show
Unused Code introduced by
The variable widget seems to be never used. Consider removing it.
Loading history...
1415
  is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space");
1416
  cm.setCursor(Pos(1, 1));
1417
  cm.execCommand("goLineDown");
1418
  eqPos(cm.getCursor(), Pos(2, 1));
1419
  cm.execCommand("goLineUp");
1420
  eqPos(cm.getCursor(), Pos(1, 1));
1421
});
1422
1423
testCM("lineWidgetFocus", function(cm) {
1424
  var place = document.getElementById("testground");
1425
  place.className = "offscreen";
1426
  try {
1427
    addDoc(cm, 500, 10);
1428
    var node = document.createElement("input");
1429
    var widget = cm.addLineWidget(1, node);
0 ignored issues
show
Unused Code introduced by
The variable widget seems to be never used. Consider removing it.
Loading history...
1430
    node.focus();
1431
    eq(document.activeElement, node);
1432
    cm.replaceRange("new stuff", Pos(1, 0));
1433
    eq(document.activeElement, node);
1434
  } finally {
1435
    place.className = "";
1436
  }
1437
});
1438
1439
testCM("lineWidgetCautiousRedraw", function(cm) {
1440
  var node = document.createElement("div");
1441
  node.innerHTML = "hahah";
1442
  var w = cm.addLineWidget(0, node);
1443
  var redrawn = false;
1444
  w.on("redraw", function() { redrawn = true; });
1445
  cm.replaceSelection("0");
1446
  is(!redrawn);
1447
}, {value: "123\n456"});
1448
1449
1450
var knownScrollbarWidth;
1451
function scrollbarWidth(measure) {
0 ignored issues
show
Unused Code introduced by
The parameter measure is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1452
  if (knownScrollbarWidth != null) return knownScrollbarWidth;
0 ignored issues
show
Best Practice introduced by
Comparing knownScrollbarWidth to null using the != operator is not safe. Consider using !== instead.
Loading history...
1453
  var div = document.createElement('div');
1454
  div.style.cssText = "width: 50px; height: 50px; overflow-x: scroll";
1455
  document.body.appendChild(div);
1456
  knownScrollbarWidth = div.offsetHeight - div.clientHeight;
1457
  document.body.removeChild(div);
1458
  return knownScrollbarWidth || 0;
1459
}
1460
1461
testCM("lineWidgetChanged", function(cm) {
1462
  addDoc(cm, 2, 300);
1463
  var halfScrollbarWidth = scrollbarWidth(cm.display.measure)/2;
1464
  cm.setOption('lineNumbers', true);
1465
  cm.setSize(600, cm.defaultTextHeight() * 50);
1466
  cm.scrollTo(null, cm.heightAtLine(125, "local"));
1467
1468
  var expectedWidgetHeight = 60;
1469
  var expectedLinesInWidget = 3;
1470
  function w() {
1471
    var node = document.createElement("div");
1472
    // we use these children with just under half width of the line to check measurements are made with correct width
1473
    // when placed in the measure div.
1474
    // 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.
1475
    // If the widget is measured at a width much wider than it is displayed at, the overHalf children will combine and break the test.
1476
    // Note that this test only checks widgets where coverGutter is true, because these require extra styling to get the width right.
1477
    // It may also be worthwhile to check this for non-coverGutter widgets.
1478
    // Visually:
1479
    // Good:
1480
    // | ------------- display width ------------- |
1481
    // | ------- widget-width when measured ------ |
1482
    // | | -- under-half -- | | -- under-half -- | | 
1483
    // | | --- over-half --- |                     |
1484
    // | | --- over-half --- |                     |
1485
    // Height: measured as 3 lines, same as it will be when actually displayed
1486
1487
    // Bad (too narrow):
1488
    // | ------------- display width ------------- |
1489
    // | ------ widget-width when measured ----- |  < -- uh oh
1490
    // | | -- under-half -- |                    |
1491
    // | | -- under-half -- |                    |  < -- when measured, shoved to next line
1492
    // | | --- over-half --- |                   |
1493
    // | | --- over-half --- |                   |
1494
    // Height: measured as 4 lines, more than expected . Will be displayed as 3 lines!
1495
1496
    // Bad (too wide):
1497
    // | ------------- display width ------------- |
1498
    // | -------- widget-width when measured ------- | < -- uh oh
1499
    // | | -- under-half -- | | -- under-half -- |   | 
1500
    // | | --- over-half --- | | --- over-half --- | | < -- when measured, combined on one line
1501
    // Height: measured as 2 lines, less than expected. Will be displayed as 3 lines!
1502
1503
    var barelyUnderHalfWidthHtml = '<div style="display: inline-block; height: 1px; width: '+(285 - halfScrollbarWidth)+'px;"></div>';
1504
    var barelyOverHalfWidthHtml = '<div style="display: inline-block; height: 1px; width: '+(305 - halfScrollbarWidth)+'px;"></div>';
1505
    node.innerHTML = new Array(3).join(barelyUnderHalfWidthHtml) + new Array(3).join(barelyOverHalfWidthHtml);
1506
    node.style.cssText = "background: yellow;font-size:0;line-height: " + (expectedWidgetHeight/expectedLinesInWidget) + "px;";
1507
    return node;
1508
  }
1509
  var info0 = cm.getScrollInfo();
1510
  var w0 = cm.addLineWidget(0, w(), { coverGutter: true });
1511
  var w150 = cm.addLineWidget(150, w(), { coverGutter: true });
1512
  var w300 = cm.addLineWidget(300, w(), { coverGutter: true });
1513
  var info1 = cm.getScrollInfo();
1514
  eq(info0.height + (3 * expectedWidgetHeight), info1.height);
1515
  eq(info0.top + expectedWidgetHeight, info1.top);
1516
  expectedWidgetHeight = 12;
1517
  w0.node.style.lineHeight = w150.node.style.lineHeight = w300.node.style.lineHeight = (expectedWidgetHeight/expectedLinesInWidget) + "px";
1518
  w0.changed(); w150.changed(); w300.changed();
1519
  var info2 = cm.getScrollInfo();
1520
  eq(info0.height + (3 * expectedWidgetHeight), info2.height);
1521
  eq(info0.top + expectedWidgetHeight, info2.top);
1522
});
1523
1524
testCM("getLineNumber", function(cm) {
1525
  addDoc(cm, 2, 20);
1526
  var h1 = cm.getLineHandle(1);
1527
  eq(cm.getLineNumber(h1), 1);
1528
  cm.replaceRange("hi\nbye\n", Pos(0, 0));
1529
  eq(cm.getLineNumber(h1), 3);
1530
  cm.setValue("");
1531
  eq(cm.getLineNumber(h1), null);
1532
});
1533
1534
testCM("jumpTheGap", function(cm) {
1535
  if (phantom) return;
1536
  var longLine = "abcdef ghiklmnop qrstuvw xyz ";
1537
  longLine += longLine; longLine += longLine; longLine += longLine;
1538
  cm.replaceRange(longLine, Pos(2, 0), Pos(2));
1539
  cm.setSize("200px", null);
1540
  cm.getWrapperElement().style.lineHeight = 2;
1541
  cm.refresh();
1542
  cm.setCursor(Pos(0, 1));
1543
  cm.execCommand("goLineDown");
1544
  eqPos(cm.getCursor(), Pos(1, 1));
1545
  cm.execCommand("goLineDown");
1546
  eqPos(cm.getCursor(), Pos(2, 1));
1547
  cm.execCommand("goLineDown");
1548
  eq(cm.getCursor().line, 2);
1549
  is(cm.getCursor().ch > 1);
1550
  cm.execCommand("goLineUp");
1551
  eqPos(cm.getCursor(), Pos(2, 1));
1552
  cm.execCommand("goLineUp");
1553
  eqPos(cm.getCursor(), Pos(1, 1));
1554
  var node = document.createElement("div");
1555
  node.innerHTML = "hi"; node.style.height = "30px";
1556
  cm.addLineWidget(0, node);
1557
  cm.addLineWidget(1, node.cloneNode(true), {above: true});
1558
  cm.setCursor(Pos(0, 2));
1559
  cm.execCommand("goLineDown");
1560
  eqPos(cm.getCursor(), Pos(1, 2));
1561
  cm.execCommand("goLineUp");
1562
  eqPos(cm.getCursor(), Pos(0, 2));
1563
}, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"});
1564
1565
testCM("addLineClass", function(cm) {
1566
  function cls(line, text, bg, wrap, gutter) {
1567
    var i = cm.lineInfo(line);
1568
    eq(i.textClass, text);
1569
    eq(i.bgClass, bg);
1570
    eq(i.wrapClass, wrap);
1571
    if (typeof i.handle.gutterClass !== 'undefined') {
1572
        eq(i.handle.gutterClass, gutter);
1573
    }
1574
  }
1575
  cm.addLineClass(0, "text", "foo");
1576
  cm.addLineClass(0, "text", "bar");
1577
  cm.addLineClass(1, "background", "baz");
1578
  cm.addLineClass(1, "wrap", "foo");
1579
  cm.addLineClass(1, "gutter", "gutter-class");
1580
  cls(0, "foo bar", null, null, null);
1581
  cls(1, null, "baz", "foo", "gutter-class");
1582
  var lines = cm.display.lineDiv;
1583
  eq(byClassName(lines, "foo").length, 2);
1584
  eq(byClassName(lines, "bar").length, 1);
1585
  eq(byClassName(lines, "baz").length, 1);
1586
  eq(byClassName(lines, "gutter-class").length, 2); // Gutter classes are reflected in 2 nodes
1587
  cm.removeLineClass(0, "text", "foo");
1588
  cls(0, "bar", null, null, null);
1589
  cm.removeLineClass(0, "text", "foo");
1590
  cls(0, "bar", null, null, null);
1591
  cm.removeLineClass(0, "text", "bar");
1592
  cls(0, null, null, null);
1593
1594
  cm.addLineClass(1, "wrap", "quux");
1595
  cls(1, null, "baz", "foo quux", "gutter-class");
1596
  cm.removeLineClass(1, "wrap");
1597
  cls(1, null, "baz", null, "gutter-class");
1598
  cm.removeLineClass(1, "gutter", "gutter-class");
1599
  eq(byClassName(lines, "gutter-class").length, 0);
1600
  cls(1, null, "baz", null, null);
1601
1602
  cm.addLineClass(1, "gutter", "gutter-class");
1603
  cls(1, null, "baz", null, "gutter-class");
1604
  cm.removeLineClass(1, "gutter", "gutter-class");
1605
  cls(1, null, "baz", null, null);
1606
1607
}, {value: "hohoho\n", lineNumbers: true});
1608
1609
testCM("atomicMarker", function(cm) {
1610
  addDoc(cm, 10, 10);
1611
  function atom(ll, cl, lr, cr, li, ri) {
1612
    return cm.markText(Pos(ll, cl), Pos(lr, cr),
1613
                       {atomic: true, inclusiveLeft: li, inclusiveRight: ri});
1614
  }
1615
  var m = atom(0, 1, 0, 5);
1616
  cm.setCursor(Pos(0, 1));
1617
  cm.execCommand("goCharRight");
1618
  eqPos(cm.getCursor(), Pos(0, 5));
1619
  cm.execCommand("goCharLeft");
1620
  eqPos(cm.getCursor(), Pos(0, 1));
1621
  m.clear();
1622
  m = atom(0, 0, 0, 5, true);
1623
  eqPos(cm.getCursor(), Pos(0, 5), "pushed out");
1624
  cm.execCommand("goCharLeft");
1625
  eqPos(cm.getCursor(), Pos(0, 5));
1626
  m.clear();
1627
  m = atom(8, 4, 9, 10, false, true);
1628
  cm.setCursor(Pos(9, 8));
1629
  eqPos(cm.getCursor(), Pos(8, 4), "set");
1630
  cm.execCommand("goCharRight");
1631
  eqPos(cm.getCursor(), Pos(8, 4), "char right");
1632
  cm.execCommand("goLineDown");
1633
  eqPos(cm.getCursor(), Pos(8, 4), "line down");
1634
  cm.execCommand("goCharLeft");
1635
  eqPos(cm.getCursor(), Pos(8, 3));
1636
  m.clear();
1637
  m = atom(1, 1, 3, 8);
0 ignored issues
show
Unused Code introduced by
The assignment to variable m seems to be never used. Consider removing it.
Loading history...
1638
  cm.setCursor(Pos(0, 0));
1639
  cm.setCursor(Pos(2, 0));
1640
  eqPos(cm.getCursor(), Pos(3, 8));
1641
  cm.execCommand("goCharLeft");
1642
  eqPos(cm.getCursor(), Pos(1, 1));
1643
  cm.execCommand("goCharRight");
1644
  eqPos(cm.getCursor(), Pos(3, 8));
1645
  cm.execCommand("goLineUp");
1646
  eqPos(cm.getCursor(), Pos(1, 1));
1647
  cm.execCommand("goLineDown");
1648
  eqPos(cm.getCursor(), Pos(3, 8));
1649
  cm.execCommand("delCharBefore");
1650
  eq(cm.getValue().length, 80, "del chunk");
1651
  m = atom(3, 0, 5, 5);
1652
  cm.setCursor(Pos(3, 0));
1653
  cm.execCommand("delWordAfter");
1654
  eq(cm.getValue().length, 53, "del chunk");
1655
});
1656
1657
testCM("selectionBias", function(cm) {
1658
  cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true});
1659
  cm.setCursor(Pos(0, 2));
1660
  eqPos(cm.getCursor(), Pos(0, 3));
1661
  cm.setCursor(Pos(0, 2));
1662
  eqPos(cm.getCursor(), Pos(0, 1));
1663
  cm.setCursor(Pos(0, 2), null, {bias: -1});
1664
  eqPos(cm.getCursor(), Pos(0, 1));
1665
  cm.setCursor(Pos(0, 4));
1666
  cm.setCursor(Pos(0, 2), null, {bias: 1});
1667
  eqPos(cm.getCursor(), Pos(0, 3));
1668
}, {value: "12345"});
1669
1670
testCM("selectionHomeEnd", function(cm) {
1671
  cm.markText(Pos(1, 0), Pos(1, 1), {atomic: true, inclusiveLeft: true});
1672
  cm.markText(Pos(1, 3), Pos(1, 4), {atomic: true, inclusiveRight: true});
1673
  cm.setCursor(Pos(1, 2));
1674
  cm.execCommand("goLineStart");
1675
  eqPos(cm.getCursor(), Pos(1, 1));
1676
  cm.execCommand("goLineEnd");
1677
  eqPos(cm.getCursor(), Pos(1, 3));
1678
}, {value: "ab\ncdef\ngh"});
1679
1680
testCM("readOnlyMarker", function(cm) {
1681
  function mark(ll, cl, lr, cr, at) {
1682
    return cm.markText(Pos(ll, cl), Pos(lr, cr),
1683
                       {readOnly: true, atomic: at});
1684
  }
1685
  var m = mark(0, 1, 0, 4);
1686
  cm.setCursor(Pos(0, 2));
1687
  cm.replaceSelection("hi", "end");
1688
  eqPos(cm.getCursor(), Pos(0, 2));
1689
  eq(cm.getLine(0), "abcde");
1690
  cm.execCommand("selectAll");
1691
  cm.replaceSelection("oops", "around");
1692
  eq(cm.getValue(), "oopsbcd");
1693
  cm.undo();
1694
  eqPos(m.find().from, Pos(0, 1));
1695
  eqPos(m.find().to, Pos(0, 4));
1696
  m.clear();
1697
  cm.setCursor(Pos(0, 2));
1698
  cm.replaceSelection("hi", "around");
1699
  eq(cm.getLine(0), "abhicde");
1700
  eqPos(cm.getCursor(), Pos(0, 4));
1701
  m = mark(0, 2, 2, 2, true);
0 ignored issues
show
Unused Code introduced by
The assignment to variable m seems to be never used. Consider removing it.
Loading history...
1702
  cm.setSelection(Pos(1, 1), Pos(2, 4));
1703
  cm.replaceSelection("t", "end");
1704
  eqPos(cm.getCursor(), Pos(2, 3));
1705
  eq(cm.getLine(2), "klto");
1706
  cm.execCommand("goCharLeft");
1707
  cm.execCommand("goCharLeft");
1708
  eqPos(cm.getCursor(), Pos(0, 2));
1709
  cm.setSelection(Pos(0, 1), Pos(0, 3));
1710
  cm.replaceSelection("xx", "around");
1711
  eqPos(cm.getCursor(), Pos(0, 3));
1712
  eq(cm.getLine(0), "axxhicde");
1713
}, {value: "abcde\nfghij\nklmno\n"});
1714
1715
testCM("dirtyBit", function(cm) {
1716
  eq(cm.isClean(), true);
1717
  cm.replaceSelection("boo", null, "test");
1718
  eq(cm.isClean(), false);
1719
  cm.undo();
1720
  eq(cm.isClean(), true);
1721
  cm.replaceSelection("boo", null, "test");
1722
  cm.replaceSelection("baz", null, "test");
1723
  cm.undo();
1724
  eq(cm.isClean(), false);
1725
  cm.markClean();
1726
  eq(cm.isClean(), true);
1727
  cm.undo();
1728
  eq(cm.isClean(), false);
1729
  cm.redo();
1730
  eq(cm.isClean(), true);
1731
});
1732
1733
testCM("changeGeneration", function(cm) {
1734
  cm.replaceSelection("x");
1735
  var softGen = cm.changeGeneration();
1736
  cm.replaceSelection("x");
1737
  cm.undo();
1738
  eq(cm.getValue(), "");
1739
  is(!cm.isClean(softGen));
1740
  cm.replaceSelection("x");
1741
  var hardGen = cm.changeGeneration(true);
1742
  cm.replaceSelection("x");
1743
  cm.undo();
1744
  eq(cm.getValue(), "x");
1745
  is(cm.isClean(hardGen));
1746
});
1747
1748
testCM("addKeyMap", function(cm) {
1749
  function sendKey(code) {
1750
    cm.triggerOnKeyDown({type: "keydown", keyCode: code,
1751
                         preventDefault: function(){}, stopPropagation: function(){}});
1752
  }
1753
1754
  sendKey(39);
1755
  eqPos(cm.getCursor(), Pos(0, 1));
1756
  var test = 0;
1757
  var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }}
1758
  cm.addKeyMap(map1);
1759
  sendKey(39);
1760
  eqPos(cm.getCursor(), Pos(0, 1));
1761
  eq(test, 1);
1762
  cm.addKeyMap(map2, true);
1763
  sendKey(39);
1764
  eq(test, 2);
1765
  cm.removeKeyMap(map1);
1766
  sendKey(39);
1767
  eq(test, 12);
1768
  cm.removeKeyMap(map2);
1769
  sendKey(39);
1770
  eq(test, 12);
1771
  eqPos(cm.getCursor(), Pos(0, 2));
1772
  cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"});
1773
  sendKey(39);
1774
  eq(test, 55);
1775
  cm.removeKeyMap("mymap");
1776
  sendKey(39);
1777
  eqPos(cm.getCursor(), Pos(0, 3));
1778
}, {value: "abc"});
1779
1780
testCM("findPosH", function(cm) {
1781
  forEach([{from: Pos(0, 0), to: Pos(0, 1), by: 1},
1782
           {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true},
1783
           {from: Pos(0, 0), to: Pos(0, 4), by: 1, unit: "word"},
1784
           {from: Pos(0, 0), to: Pos(0, 8), by: 2, unit: "word"},
1785
           {from: Pos(0, 0), to: Pos(2, 0), by: 20, unit: "word", hitSide: true},
1786
           {from: Pos(0, 7), to: Pos(0, 5), by: -1, unit: "word"},
1787
           {from: Pos(0, 4), to: Pos(0, 8), by: 1, unit: "word"},
1788
           {from: Pos(1, 0), to: Pos(1, 18), by: 3, unit: "word"},
1789
           {from: Pos(1, 22), to: Pos(1, 5), by: -3, unit: "word"},
1790
           {from: Pos(1, 15), to: Pos(1, 10), by: -5},
1791
           {from: Pos(1, 15), to: Pos(1, 10), by: -5, unit: "column"},
1792
           {from: Pos(1, 15), to: Pos(1, 0), by: -50, unit: "column", hitSide: true},
1793
           {from: Pos(1, 15), to: Pos(1, 24), by: 50, unit: "column", hitSide: true},
1794
           {from: Pos(1, 15), to: Pos(2, 0), by: 50, hitSide: true}], function(t) {
1795
    var r = cm.findPosH(t.from, t.by, t.unit || "char");
1796
    eqPos(r, t.to);
1797
    eq(!!r.hitSide, !!t.hitSide);
1798
  });
1799
}, {value: "line one\nline two.something.other\n"});
1800
1801
testCM("beforeChange", function(cm) {
1802
  cm.on("beforeChange", function(cm, change) {
1803
    var text = [];
1804
    for (var i = 0; i < change.text.length; ++i)
1805
      text.push(change.text[i].replace(/\s/g, "_"));
1806
    change.update(null, null, text);
1807
  });
1808
  cm.setValue("hello, i am a\nnew document\n");
1809
  eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
1810
  CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) {
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1811
    if (change.from.line == 0) change.cancel();
0 ignored issues
show
Best Practice introduced by
Comparing change.from.line to 0 using the == operator is not safe. Consider using === instead.
Loading history...
1812
  });
1813
  cm.setValue("oops"); // Canceled
1814
  eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
1815
  cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0));
1816
  eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey");
1817
}, {value: "abcdefghijk"});
1818
1819
testCM("beforeChangeUndo", function(cm) {
1820
  cm.replaceRange("hi", Pos(0, 0), Pos(0));
1821
  cm.replaceRange("bye", Pos(0, 0), Pos(0));
1822
  eq(cm.historySize().undo, 2);
1823
  cm.on("beforeChange", function(cm, change) {
1824
    is(!change.update);
1825
    change.cancel();
1826
  });
1827
  cm.undo();
1828
  eq(cm.historySize().undo, 0);
1829
  eq(cm.getValue(), "bye\ntwo");
1830
}, {value: "one\ntwo"});
1831
1832
testCM("beforeSelectionChange", function(cm) {
1833
  function notAtEnd(cm, pos) {
1834
    var len = cm.getLine(pos.line).length;
1835
    if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1);
1836
    return pos;
1837
  }
1838
  cm.on("beforeSelectionChange", function(cm, obj) {
1839
    obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor),
1840
                 head: notAtEnd(cm, obj.ranges[0].head)}]);
1841
  });
1842
1843
  addDoc(cm, 10, 10);
1844
  cm.execCommand("goLineEnd");
1845
  eqPos(cm.getCursor(), Pos(0, 9));
1846
  cm.execCommand("selectAll");
1847
  eqPos(cm.getCursor("start"), Pos(0, 0));
1848
  eqPos(cm.getCursor("end"), Pos(9, 9));
1849
});
1850
1851
testCM("change_removedText", function(cm) {
1852
  cm.setValue("abc\ndef");
1853
1854
  var removedText = [];
1855
  cm.on("change", function(cm, change) {
1856
    removedText.push(change.removed);
1857
  });
1858
1859
  cm.operation(function() {
1860
    cm.replaceRange("xyz", Pos(0, 0), Pos(1,1));
1861
    cm.replaceRange("123", Pos(0,0));
1862
  });
1863
1864
  eq(removedText.length, 2);
1865
  eq(removedText[0].join("\n"), "abc\nd");
1866
  eq(removedText[1].join("\n"), "");
1867
1868
  var removedText = [];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable removedText already seems to be declared on line 1854. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1869
  cm.undo();
1870
  eq(removedText.length, 2);
1871
  eq(removedText[0].join("\n"), "123");
1872
  eq(removedText[1].join("\n"), "xyz");
1873
1874
  var removedText = [];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable removedText already seems to be declared on line 1854. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
1875
  cm.redo();
1876
  eq(removedText.length, 2);
1877
  eq(removedText[0].join("\n"), "abc\nd");
1878
  eq(removedText[1].join("\n"), "");
1879
});
1880
1881
testCM("lineStyleFromMode", function(cm) {
1882
  CodeMirror.defineMode("test_mode", function() {
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1883
    return {token: function(stream) {
1884
      if (stream.match(/^\[[^\]]*\]/)) return "  line-brackets  ";
1885
      if (stream.match(/^\([^\)]*\)/)) return "  line-background-parens  ";
1886
      if (stream.match(/^<[^>]*>/)) return "  span  line-line  line-background-bg  ";
1887
      stream.match(/^\s+|^\S+/);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1888
    }};
1889
  });
1890
  cm.setOption("mode", "test_mode");
1891
  var bracketElts = byClassName(cm.getWrapperElement(), "brackets");
1892
  eq(bracketElts.length, 1, "brackets count");
1893
  eq(bracketElts[0].nodeName, "PRE");
1894
  is(!/brackets.*brackets/.test(bracketElts[0].className));
1895
  var parenElts = byClassName(cm.getWrapperElement(), "parens");
1896
  eq(parenElts.length, 1, "parens count");
1897
  eq(parenElts[0].nodeName, "DIV");
1898
  is(!/parens.*parens/.test(parenElts[0].className));
1899
  eq(parenElts[0].parentElement.nodeName, "DIV");
1900
1901
  eq(byClassName(cm.getWrapperElement(), "bg").length, 1);
1902
  eq(byClassName(cm.getWrapperElement(), "line").length, 1);
1903
  var spanElts = byClassName(cm.getWrapperElement(), "cm-span");
1904
  eq(spanElts.length, 2);
1905
  is(/^\s*cm-span\s*$/.test(spanElts[0].className));
1906
}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: <tag> <tag>"});
1907
1908
testCM("lineStyleFromBlankLine", function(cm) {
1909
  CodeMirror.defineMode("lineStyleFromBlankLine_mode", function() {
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1910
    return {token: function(stream) { stream.skipToEnd(); return "comment"; },
1911
            blankLine: function() { return "line-blank"; }};
1912
  });
1913
  cm.setOption("mode", "lineStyleFromBlankLine_mode");
1914
  var blankElts = byClassName(cm.getWrapperElement(), "blank");
1915
  eq(blankElts.length, 1);
1916
  eq(blankElts[0].nodeName, "PRE");
1917
  cm.replaceRange("x", Pos(1, 0));
1918
  blankElts = byClassName(cm.getWrapperElement(), "blank");
1919
  eq(blankElts.length, 0);
1920
}, {value: "foo\n\nbar"});
1921
1922
CodeMirror.registerHelper("xxx", "a", "A");
1923
CodeMirror.registerHelper("xxx", "b", "B");
1924
CodeMirror.defineMode("yyy", function() {
1925
  return {
1926
    token: function(stream) { stream.skipToEnd(); },
1927
    xxx: ["a", "b", "q"]
1928
  };
1929
});
1930
CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C");
1931
1932
testCM("helpers", function(cm) {
1933
  cm.setOption("mode", "yyy");
1934
  eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B");
1935
  cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}});
1936
  eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C");
1937
  cm.setOption("mode", "javascript");
1938
  eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "");
1939
});
1940
1941
testCM("selectionHistory", function(cm) {
1942
  for (var i = 0; i < 3; i++) {
1943
    cm.setExtending(true);
1944
    cm.execCommand("goCharRight");
1945
    cm.setExtending(false);
1946
    cm.execCommand("goCharRight");
1947
    cm.execCommand("goCharRight");
1948
  }
1949
  cm.execCommand("undoSelection");
1950
  eq(cm.getSelection(), "c");
1951
  cm.execCommand("undoSelection");
1952
  eq(cm.getSelection(), "");
1953
  eqPos(cm.getCursor(), Pos(0, 4));
1954
  cm.execCommand("undoSelection");
1955
  eq(cm.getSelection(), "b");
1956
  cm.execCommand("redoSelection");
1957
  eq(cm.getSelection(), "");
1958
  eqPos(cm.getCursor(), Pos(0, 4));
1959
  cm.execCommand("redoSelection");
1960
  eq(cm.getSelection(), "c");
1961
  cm.execCommand("redoSelection");
1962
  eq(cm.getSelection(), "");
1963
  eqPos(cm.getCursor(), Pos(0, 6));
1964
}, {value: "a b c d"});
1965
1966
testCM("selectionChangeReducesRedo", function(cm) {
1967
  cm.replaceSelection("X");
1968
  cm.execCommand("goCharRight");
1969
  cm.undoSelection();
1970
  cm.execCommand("selectAll");
1971
  cm.undoSelection();
1972
  eq(cm.getValue(), "Xabc");
1973
  eqPos(cm.getCursor(), Pos(0, 1));
1974
  cm.undoSelection();
1975
  eq(cm.getValue(), "abc");
1976
}, {value: "abc"});
1977
1978
testCM("selectionHistoryNonOverlapping", function(cm) {
1979
  cm.setSelection(Pos(0, 0), Pos(0, 1));
1980
  cm.setSelection(Pos(0, 2), Pos(0, 3));
1981
  cm.execCommand("undoSelection");
1982
  eqPos(cm.getCursor("anchor"), Pos(0, 0));
1983
  eqPos(cm.getCursor("head"), Pos(0, 1));
1984
}, {value: "1234"});
1985
1986
testCM("cursorMotionSplitsHistory", function(cm) {
1987
  cm.replaceSelection("a");
1988
  cm.execCommand("goCharRight");
1989
  cm.replaceSelection("b");
1990
  cm.replaceSelection("c");
1991
  cm.undo();
1992
  eq(cm.getValue(), "a1234");
1993
  eqPos(cm.getCursor(), Pos(0, 2));
1994
  cm.undo();
1995
  eq(cm.getValue(), "1234");
1996
  eqPos(cm.getCursor(), Pos(0, 0));
1997
}, {value: "1234"});
1998
1999
testCM("selChangeInOperationDoesNotSplit", function(cm) {
2000
  for (var i = 0; i < 4; i++) {
2001
    cm.operation(function() {
2002
      cm.replaceSelection("x");
2003
      cm.setCursor(Pos(0, cm.getCursor().ch - 1));
2004
    });
2005
  }
2006
  eqPos(cm.getCursor(), Pos(0, 0));
2007
  eq(cm.getValue(), "xxxxa");
2008
  cm.undo();
2009
  eq(cm.getValue(), "a");
2010
}, {value: "a"});
2011
2012
testCM("alwaysMergeSelEventWithChangeOrigin", function(cm) {
2013
  cm.replaceSelection("U", null, "foo");
2014
  cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "foo"});
2015
  cm.undoSelection();
2016
  eq(cm.getValue(), "a");
2017
  cm.replaceSelection("V", null, "foo");
2018
  cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "bar"});
2019
  cm.undoSelection();
2020
  eq(cm.getValue(), "Va");
2021
}, {value: "a"});
2022
2023
testCM("getTokenAt", function(cm) {
2024
  var tokPlus = cm.getTokenAt(Pos(0, 2));
2025
  eq(tokPlus.type, "operator");
2026
  eq(tokPlus.string, "+");
2027
  var toks = cm.getLineTokens(0);
2028
  eq(toks.length, 3);
2029
  forEach([["number", "1"], ["operator", "+"], ["number", "2"]], function(expect, i) {
2030
    eq(toks[i].type, expect[0]);
2031
    eq(toks[i].string, expect[1]);
2032
  });
2033
}, {value: "1+2", mode: "javascript"});
2034
2035
testCM("getTokenTypeAt", function(cm) {
2036
  eq(cm.getTokenTypeAt(Pos(0, 0)), "number");
2037
  eq(cm.getTokenTypeAt(Pos(0, 6)), "string");
2038
  cm.addOverlay({
2039
    token: function(stream) {
2040
      if (stream.match("foo")) return "foo";
2041
      else stream.next();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
2042
    }
2043
  });
2044
  eq(byClassName(cm.getWrapperElement(), "cm-foo").length, 1);
2045
  eq(cm.getTokenTypeAt(Pos(0, 6)), "string");
2046
}, {value: "1 + 'foo'", mode: "javascript"});
2047
2048
testCM("resizeLineWidget", function(cm) {
2049
  addDoc(cm, 200, 3);
2050
  var widget = document.createElement("pre");
2051
  widget.innerHTML = "imwidget";
2052
  widget.style.background = "yellow";
2053
  cm.addLineWidget(1, widget, {noHScroll: true});
2054
  cm.setSize(40);
2055
  is(widget.parentNode.offsetWidth < 42);
2056
});
2057
2058
testCM("combinedOperations", function(cm) {
2059
  var place = document.getElementById("testground");
2060
  var other = CodeMirror(place, {value: "123"});
2061
  try {
2062
    cm.operation(function() {
2063
      cm.addLineClass(0, "wrap", "foo");
2064
      other.addLineClass(0, "wrap", "foo");
2065
    });
2066
    eq(byClassName(cm.getWrapperElement(), "foo").length, 1);
2067
    eq(byClassName(other.getWrapperElement(), "foo").length, 1);
2068
    cm.operation(function() {
2069
      cm.removeLineClass(0, "wrap", "foo");
2070
      other.removeLineClass(0, "wrap", "foo");
2071
    });
2072
    eq(byClassName(cm.getWrapperElement(), "foo").length, 0);
2073
    eq(byClassName(other.getWrapperElement(), "foo").length, 0);
2074
  } finally {
2075
    place.removeChild(other.getWrapperElement());
2076
  }
2077
}, {value: "abc"});
2078
2079
testCM("eventOrder", function(cm) {
2080
  var seen = [];
2081
  cm.on("change", function() {
2082
    if (!seen.length) cm.replaceSelection(".");
2083
    seen.push("change");
2084
  });
2085
  cm.on("cursorActivity", function() {
2086
    cm.replaceSelection("!");
2087
    seen.push("activity");
2088
  });
2089
  cm.replaceSelection("/");
2090
  eq(seen.join(","), "change,change,activity,change");
2091
});
2092
2093
testCM("splitSpaces_nonspecial", function(cm) {
2094
  eq(byClassName(cm.getWrapperElement(), "cm-invalidchar").length, 0);
2095
}, {
2096
  specialChars: /[\u00a0]/,
2097
  value: "spaces ->            <- between"
2098
});
2099
2100
test("core_rmClass", function() {
2101
  var node = document.createElement("div");
2102
  node.className = "foo-bar baz-quux yadda";
2103
  CodeMirror.rmClass(node, "quux");
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2104
  eq(node.className, "foo-bar baz-quux yadda");
2105
  CodeMirror.rmClass(node, "baz-quux");
2106
  eq(node.className, "foo-bar yadda");
2107
  CodeMirror.rmClass(node, "yadda");
2108
  eq(node.className, "foo-bar");
2109
  CodeMirror.rmClass(node, "foo-bar");
2110
  eq(node.className, "");
2111
  node.className = " foo ";
2112
  CodeMirror.rmClass(node, "foo");
2113
  eq(node.className, "");
2114
});
2115
2116
test("core_addClass", function() {
2117
  var node = document.createElement("div");
2118
  CodeMirror.addClass(node, "a");
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2119
  eq(node.className, "a");
2120
  CodeMirror.addClass(node, "a");
2121
  eq(node.className, "a");
2122
  CodeMirror.addClass(node, "b");
2123
  eq(node.className, "a b");
2124
  CodeMirror.addClass(node, "a");
2125
  CodeMirror.addClass(node, "b");
2126
  eq(node.className, "a b");
2127
});
2128
2129
testCM("lineSeparator", function(cm) {
2130
  eq(cm.lineCount(), 3);
2131
  eq(cm.getLine(1), "bar\r");
2132
  eq(cm.getLine(2), "baz\rquux");
2133
  cm.setOption("lineSeparator", "\r");
2134
  eq(cm.lineCount(), 5);
2135
  eq(cm.getLine(4), "quux");
2136
  eq(cm.getValue(), "foo\rbar\r\rbaz\rquux");
2137
  eq(cm.getValue("\n"), "foo\nbar\n\nbaz\nquux");
2138
  cm.setOption("lineSeparator", null);
2139
  cm.setValue("foo\nbar\r\nbaz\rquux");
2140
  eq(cm.lineCount(), 4);
2141
}, {value: "foo\nbar\r\nbaz\rquux",
2142
    lineSeparator: "\n"});
2143