Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/cm/codemirror.js

Issue 2166603002: DevTools: roll CodeMirror (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Revert unnecessary typeIn change Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE 2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3 3
4 // This is CodeMirror (http://codemirror.net), a code editor 4 // This is CodeMirror (http://codemirror.net), a code editor
5 // implemented in JavaScript on top of the browser's DOM. 5 // implemented in JavaScript on top of the browser's DOM.
6 // 6 //
7 // You can find some technical background for some of the code below 7 // You can find some technical background for some of the code below
8 // at http://marijnhaverbeke.nl/blog/#cm-internals . 8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9 9
10 (function(mod) { 10 (function(mod) {
11 if (typeof exports == "object" && typeof module == "object") // CommonJS 11 if (typeof exports == "object" && typeof module == "object") // CommonJS
12 module.exports = mod(); 12 module.exports = mod();
13 else if (typeof define == "function" && define.amd) // AMD 13 else if (typeof define == "function" && define.amd) // AMD
14 return define([], mod); 14 return define([], mod);
15 else // Plain browser env 15 else // Plain browser env
16 this.CodeMirror = mod(); 16 (this || window).CodeMirror = mod();
17 })(function() { 17 })(function() {
18 "use strict"; 18 "use strict";
19 19
20 // BROWSER SNIFFING 20 // BROWSER SNIFFING
21 21
22 // Kludges for bugs and behavior differences that can't be feature 22 // Kludges for bugs and behavior differences that can't be feature
23 // detected are enabled based on userAgent etc sniffing. 23 // detected are enabled based on userAgent etc sniffing.
24 var userAgent = navigator.userAgent;
25 var platform = navigator.platform;
24 26
25 var gecko = /gecko\/\d/i.test(navigator.userAgent); 27 var gecko = /gecko\/\d/i.test(userAgent);
26 // ie_uptoN means Internet Explorer version N or lower 28 var ie_upto10 = /MSIE \d/.test(userAgent);
27 var ie_upto10 = /MSIE \d/.test(navigator.userAgent); 29 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
28 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent );
29 var ie = ie_upto10 || ie_11up; 30 var ie = ie_upto10 || ie_11up;
30 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); 31 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
31 var webkit = /WebKit\//.test(navigator.userAgent); 32 var webkit = /WebKit\//.test(userAgent);
32 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); 33 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
33 var chrome = /Chrome\//.test(navigator.userAgent); 34 var chrome = /Chrome\//.test(userAgent);
34 var presto = /Opera\//.test(navigator.userAgent); 35 var presto = /Opera\//.test(userAgent);
35 var safari = /Apple Computer/.test(navigator.vendor); 36 var safari = /Apple Computer/.test(navigator.vendor);
36 var khtml = /KHTML\//.test(navigator.userAgent); 37 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
37 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAge nt); 38 var phantom = /PhantomJS/.test(userAgent);
38 var phantom = /PhantomJS/.test(navigator.userAgent);
39 39
40 var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(naviga tor.userAgent); 40 var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
41 // This is woefully incomplete. Suggestions for alternative methods welcome. 41 // This is woefully incomplete. Suggestions for alternative methods welcome.
42 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i .test(navigator.userAgent); 42 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i .test(userAgent);
43 var mac = ios || /Mac/.test(navigator.platform); 43 var mac = ios || /Mac/.test(platform);
44 var windows = /win/i.test(navigator.platform); 44 var chromeOS = /\bCrOS\b/.test(userAgent);
45 var windows = /win/i.test(platform);
45 46
46 var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/ ); 47 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
47 if (presto_version) presto_version = Number(presto_version[1]); 48 if (presto_version) presto_version = Number(presto_version[1]);
48 if (presto_version && presto_version >= 15) { presto = false; webkit = true; } 49 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
49 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X 50 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
50 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || pre sto_version < 12.11)); 51 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || pre sto_version < 12.11));
51 var captureRightClick = gecko || (ie && ie_version >= 9); 52 var captureRightClick = gecko || (ie && ie_version >= 9);
52 53
53 // Optimize some code when these features are not used. 54 // Optimize some code when these features are not used.
54 var sawReadOnlySpans = false, sawCollapsedSpans = false; 55 var sawReadOnlySpans = false, sawCollapsedSpans = false;
55 56
56 // EDITOR CONSTRUCTOR 57 // EDITOR CONSTRUCTOR
57 58
58 // A CodeMirror instance represents an editor. This is the object 59 // A CodeMirror instance represents an editor. This is the object
59 // that user code is usually dealing with. 60 // that user code is usually dealing with.
60 61
61 function CodeMirror(place, options) { 62 function CodeMirror(place, options) {
62 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); 63 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
63 64
64 this.options = options = options ? copyObj(options) : {}; 65 this.options = options = options ? copyObj(options) : {};
65 // Determine effective options based on given values and defaults. 66 // Determine effective options based on given values and defaults.
66 copyObj(defaults, options, false); 67 copyObj(defaults, options, false);
67 setGuttersForLineNumbers(options); 68 setGuttersForLineNumbers(options);
68 69
69 var doc = options.value; 70 var doc = options.value;
70 if (typeof doc == "string") doc = new Doc(doc, options.mode); 71 if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.l ineSeparator);
71 this.doc = doc; 72 this.doc = doc;
72 73
73 var display = this.display = new Display(place, doc); 74 var input = new CodeMirror.inputStyles[options.inputStyle](this);
75 var display = this.display = new Display(place, doc, input);
74 display.wrapper.CodeMirror = this; 76 display.wrapper.CodeMirror = this;
75 updateGutters(this); 77 updateGutters(this);
76 themeChanged(this); 78 themeChanged(this);
77 if (options.lineWrapping) 79 if (options.lineWrapping)
78 this.display.wrapper.className += " CodeMirror-wrap"; 80 this.display.wrapper.className += " CodeMirror-wrap";
79 if (options.autofocus && !mobile) focusInput(this); 81 if (options.autofocus && !mobile) display.input.focus();
80 initScrollbars(this); 82 initScrollbars(this);
81 83
82 this.state = { 84 this.state = {
83 keyMaps: [], // stores maps added by addKeyMap 85 keyMaps: [], // stores maps added by addKeyMap
84 overlays: [], // highlighting overlays, as added by addOverlay 86 overlays: [], // highlighting overlays, as added by addOverlay
85 modeGen: 0, // bumped when mode/overlay changes, used to invalidate high lighting info 87 modeGen: 0, // bumped when mode/overlay changes, used to invalidate high lighting info
86 overwrite: false, focused: false, 88 overwrite: false,
89 delayingBlurEvent: false,
90 focused: false,
87 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode 91 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
88 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edit s in readInput 92 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edit s in input.poll
93 selectingText: false,
89 draggingText: false, 94 draggingText: false,
90 highlight: new Delayed(), // stores highlight worker timeout 95 highlight: new Delayed(), // stores highlight worker timeout
91 keySeq: null // Unfinished key sequence 96 keySeq: null, // Unfinished key sequence
97 specialChars: null
92 }; 98 };
93 99
100 var cm = this;
101
94 // Override magic textarea content restore that IE sometimes does 102 // Override magic textarea content restore that IE sometimes does
95 // on our hidden textarea on reload 103 // on our hidden textarea on reload
96 if (ie && ie_version < 11) setTimeout(bind(resetInput, this, true), 20); 104 if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(tr ue); }, 20);
97 105
98 registerEventHandlers(this); 106 registerEventHandlers(this);
99 ensureGlobalHandlers(); 107 ensureGlobalHandlers();
100 108
101 startOperation(this); 109 startOperation(this);
102 this.curOp.forceUpdate = true; 110 this.curOp.forceUpdate = true;
103 attachDoc(this, doc); 111 attachDoc(this, doc);
104 112
105 if ((options.autofocus && !mobile) || activeElt() == display.input) 113 if ((options.autofocus && !mobile) || cm.hasFocus())
106 setTimeout(bind(onFocus, this), 20); 114 setTimeout(bind(onFocus, this), 20);
107 else 115 else
108 onBlur(this); 116 onBlur(this);
109 117
110 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) 118 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
111 optionHandlers[opt](this, options[opt], Init); 119 optionHandlers[opt](this, options[opt], Init);
112 maybeUpdateLineNumberWidth(this); 120 maybeUpdateLineNumberWidth(this);
121 if (options.finishInit) options.finishInit(this);
113 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); 122 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
114 endOperation(this); 123 endOperation(this);
115 // Suppress optimizelegibility in Webkit, since it breaks text 124 // Suppress optimizelegibility in Webkit, since it breaks text
116 // measuring on line wrapping boundaries. 125 // measuring on line wrapping boundaries.
117 if (webkit && options.lineWrapping && 126 if (webkit && options.lineWrapping &&
118 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") 127 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
119 display.lineDiv.style.textRendering = "auto"; 128 display.lineDiv.style.textRendering = "auto";
120 } 129 }
121 130
122 // DISPLAY CONSTRUCTOR 131 // DISPLAY CONSTRUCTOR
123 132
124 // The display handles the DOM integration, both for input reading 133 // The display handles the DOM integration, both for input reading
125 // and content drawing. It holds references to DOM nodes and 134 // and content drawing. It holds references to DOM nodes and
126 // display-related state. 135 // display-related state.
127 136
128 function Display(place, doc) { 137 function Display(place, doc, input) {
129 var d = this; 138 var d = this;
139 this.input = input;
130 140
131 // The semihidden textarea that is focused when the editor is
132 // focused, and receives input.
133 var input = d.input = elt("textarea", null, null, "position: absolute; paddi ng: 0; width: 1px; height: 1em; outline: none");
134 // The textarea is kept positioned near the cursor to prevent the
135 // fact that it'll be scrolled into view on input from scrolling
136 // our fake cursor out of view. On webkit, when wrap=off, paste is
137 // very slow. So make the area wide instead.
138 if (webkit) input.style.width = "1000px";
139 else input.setAttribute("wrap", "off");
140 // If border: 0; -- iOS fails to open keyboard (issue #1287)
141 if (ios) input.style.border = "1px solid black";
142 input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize ", "off"); input.setAttribute("spellcheck", "false");
143
144 // Wraps and hides input textarea
145 d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative ; width: 3px; height: 0px;");
146 // Covers bottom-right square when both scrollbars are present. 141 // Covers bottom-right square when both scrollbars are present.
147 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); 142 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
148 d.scrollbarFiller.setAttribute("not-content", "true"); 143 d.scrollbarFiller.setAttribute("cm-not-content", "true");
149 // Covers bottom of gutter when coverGutterNextToScrollbar is on 144 // Covers bottom of gutter when coverGutterNextToScrollbar is on
150 // and h scrollbar is present. 145 // and h scrollbar is present.
151 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); 146 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
152 d.gutterFiller.setAttribute("not-content", "true"); 147 d.gutterFiller.setAttribute("cm-not-content", "true");
153 // Will contain the actual code, positioned to cover the viewport. 148 // Will contain the actual code, positioned to cover the viewport.
154 d.lineDiv = elt("div", null, "CodeMirror-code"); 149 d.lineDiv = elt("div", null, "CodeMirror-code");
155 // Elements are added to these to represent selection and cursors. 150 // Elements are added to these to represent selection and cursors.
156 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); 151 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
157 d.cursorDiv = elt("div", null, "CodeMirror-cursors"); 152 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
158 // A visibility: hidden element used to find the size of things. 153 // A visibility: hidden element used to find the size of things.
159 d.measure = elt("div", null, "CodeMirror-measure"); 154 d.measure = elt("div", null, "CodeMirror-measure");
160 // When lines outside of the viewport are measured, they are drawn in this. 155 // When lines outside of the viewport are measured, they are drawn in this.
161 d.lineMeasure = elt("div", null, "CodeMirror-measure"); 156 d.lineMeasure = elt("div", null, "CodeMirror-measure");
162 // Wraps everything that needs to exist inside the vertically-padded coordin ate system 157 // Wraps everything that needs to exist inside the vertically-padded coordin ate system
163 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursor Div, d.lineDiv], 158 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursor Div, d.lineDiv],
164 null, "position: relative; outline: none"); 159 null, "position: relative; outline: none");
165 // Moved around its parent to cover visible view. 160 // Moved around its parent to cover visible view.
166 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); 161 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
167 // Set to the height of the document, allowing scrolling. 162 // Set to the height of the document, allowing scrolling.
168 d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); 163 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
169 d.sizerWidth = null; 164 d.sizerWidth = null;
170 // Behavior of elts with overflow: auto and padding is 165 // Behavior of elts with overflow: auto and padding is
171 // inconsistent across browsers. This is used to ensure the 166 // inconsistent across browsers. This is used to ensure the
172 // scrollable area is big enough. 167 // scrollable area is big enough.
173 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scr ollerGap + "px; width: 1px;"); 168 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scr ollerGap + "px; width: 1px;");
174 // Will contain the gutters, if any. 169 // Will contain the gutters, if any.
175 d.gutters = elt("div", null, "CodeMirror-gutters"); 170 d.gutters = elt("div", null, "CodeMirror-gutters");
176 d.lineGutter = null; 171 d.lineGutter = null;
177 // Actual scrollable element. 172 // Actual scrollable element.
178 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-sc roll"); 173 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-sc roll");
179 d.scroller.setAttribute("tabIndex", "-1"); 174 d.scroller.setAttribute("tabIndex", "-1");
180 // The element in which the editor lives. 175 // The element in which the editor lives.
181 d.wrapper = elt("div", [d.inputDiv, d.scrollbarFiller, d.gutterFiller, d.scr oller], "CodeMirror"); 176 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "Cod eMirror");
182 177
183 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supp orted) 178 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supp orted)
184 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.pa ddingRight = 0; } 179 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.pa ddingRight = 0; }
185 // Needed to hide big blue blinking cursor on Mobile Safari 180 if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
186 if (ios) input.style.width = "0px";
187 if (!webkit) d.scroller.draggable = true;
188 // Needed to handle Tab key in KHTML
189 if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "a bsolute"; }
190 181
191 if (place) { 182 if (place) {
192 if (place.appendChild) place.appendChild(d.wrapper); 183 if (place.appendChild) place.appendChild(d.wrapper);
193 else place(d.wrapper); 184 else place(d.wrapper);
194 } 185 }
195 186
196 // Current rendered range (may be bigger than the view window). 187 // Current rendered range (may be bigger than the view window).
197 d.viewFrom = d.viewTo = doc.first; 188 d.viewFrom = d.viewTo = doc.first;
198 d.reportedViewFrom = d.reportedViewTo = doc.first; 189 d.reportedViewFrom = d.reportedViewTo = doc.first;
199 // Information about the rendered lines. 190 // Information about the rendered lines.
200 d.view = []; 191 d.view = [];
201 d.renderedView = null; 192 d.renderedView = null;
202 // Holds info about a single rendered line when it was rendered 193 // Holds info about a single rendered line when it was rendered
203 // for measurement, while not in view. 194 // for measurement, while not in view.
204 d.externalMeasured = null; 195 d.externalMeasured = null;
205 // Empty space (in pixels) above the view 196 // Empty space (in pixels) above the view
206 d.viewOffset = 0; 197 d.viewOffset = 0;
207 d.lastWrapHeight = d.lastWrapWidth = 0; 198 d.lastWrapHeight = d.lastWrapWidth = 0;
208 d.updateLineNumbers = null; 199 d.updateLineNumbers = null;
209 200
210 d.nativeBarWidth = d.barHeight = d.barWidth = 0; 201 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
211 d.scrollbarsClipped = false; 202 d.scrollbarsClipped = false;
212 203
213 // Used to only resize the line number gutter when necessary (when 204 // Used to only resize the line number gutter when necessary (when
214 // the amount of lines crosses a boundary that makes its width change) 205 // the amount of lines crosses a boundary that makes its width change)
215 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; 206 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
216 // See readInput and resetInput
217 d.prevInput = "";
218 // Set to true when a non-horizontal-scrolling line widget is 207 // Set to true when a non-horizontal-scrolling line widget is
219 // added. As an optimization, line widget aligning is skipped when 208 // added. As an optimization, line widget aligning is skipped when
220 // this is false. 209 // this is false.
221 d.alignWidgets = false; 210 d.alignWidgets = false;
222 // Flag that indicates whether we expect input to appear real soon
223 // now (after some event like 'keypress' or 'input') and are
224 // polling intensively.
225 d.pollingFast = false;
226 // Self-resetting timeout for the poller
227 d.poll = new Delayed();
228 211
229 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; 212 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
230 213
231 // Tracks when resetInput has punted to just putting a short
232 // string into the textarea instead of the full selection.
233 d.inaccurateSelection = false;
234
235 // Tracks the maximum line length so that the horizontal scrollbar 214 // Tracks the maximum line length so that the horizontal scrollbar
236 // can be kept static when scrolling. 215 // can be kept static when scrolling.
237 d.maxLine = null; 216 d.maxLine = null;
238 d.maxLineLength = 0; 217 d.maxLineLength = 0;
239 d.maxLineChanged = false; 218 d.maxLineChanged = false;
240 219
241 // Used for measuring wheel scrolling granularity 220 // Used for measuring wheel scrolling granularity
242 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; 221 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
243 222
244 // True when shift is held down. 223 // True when shift is held down.
245 d.shift = false; 224 d.shift = false;
246 225
247 // Used to track whether anything happened since the context menu 226 // Used to track whether anything happened since the context menu
248 // was opened. 227 // was opened.
249 d.selForContextMenu = null; 228 d.selForContextMenu = null;
229
230 d.activeTouch = null;
231
232 input.init(d);
250 } 233 }
251 234
252 // STATE UPDATES 235 // STATE UPDATES
253 236
254 // Used to get the editor into a consistent state again when options change. 237 // Used to get the editor into a consistent state again when options change.
255 238
256 function loadMode(cm) { 239 function loadMode(cm) {
257 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); 240 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
258 resetModeState(cm); 241 resetModeState(cm);
259 } 242 }
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; m in-height: 1px")], "CodeMirror-hscrollbar"); 404 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; m in-height: 1px")], "CodeMirror-hscrollbar");
422 place(vert); place(horiz); 405 place(vert); place(horiz);
423 406
424 on(vert, "scroll", function() { 407 on(vert, "scroll", function() {
425 if (vert.clientHeight) scroll(vert.scrollTop, "vertical"); 408 if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
426 }); 409 });
427 on(horiz, "scroll", function() { 410 on(horiz, "scroll", function() {
428 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal"); 411 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
429 }); 412 });
430 413
431 this.checkedOverlay = false; 414 this.checkedZeroWidth = false;
432 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). 415 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
433 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWi dth = "18px"; 416 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWi dth = "18px";
434 } 417 }
435 418
436 NativeScrollbars.prototype = copyObj({ 419 NativeScrollbars.prototype = copyObj({
437 update: function(measure) { 420 update: function(measure) {
438 var needsH = measure.scrollWidth > measure.clientWidth + 1; 421 var needsH = measure.scrollWidth > measure.clientWidth + 1;
439 var needsV = measure.scrollHeight > measure.clientHeight + 1; 422 var needsV = measure.scrollHeight > measure.clientHeight + 1;
440 var sWidth = measure.nativeBarWidth; 423 var sWidth = measure.nativeBarWidth;
441 424
(...skipping 14 matching lines...) Expand all
456 this.horiz.style.right = needsV ? sWidth + "px" : "0"; 439 this.horiz.style.right = needsV ? sWidth + "px" : "0";
457 this.horiz.style.left = measure.barLeft + "px"; 440 this.horiz.style.left = measure.barLeft + "px";
458 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); 441 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
459 this.horiz.firstChild.style.width = 442 this.horiz.firstChild.style.width =
460 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; 443 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
461 } else { 444 } else {
462 this.horiz.style.display = ""; 445 this.horiz.style.display = "";
463 this.horiz.firstChild.style.width = "0"; 446 this.horiz.firstChild.style.width = "0";
464 } 447 }
465 448
466 if (!this.checkedOverlay && measure.clientHeight > 0) { 449 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
467 if (sWidth == 0) this.overlayHack(); 450 if (sWidth == 0) this.zeroWidthHack();
468 this.checkedOverlay = true; 451 this.checkedZeroWidth = true;
469 } 452 }
470 453
471 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}; 454 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
472 }, 455 },
473 setScrollLeft: function(pos) { 456 setScrollLeft: function(pos) {
474 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos; 457 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
458 if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHor iz);
475 }, 459 },
476 setScrollTop: function(pos) { 460 setScrollTop: function(pos) {
477 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos; 461 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
462 if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert) ;
478 }, 463 },
479 overlayHack: function() { 464 zeroWidthHack: function() {
480 var w = mac && !mac_geMountainLion ? "12px" : "18px"; 465 var w = mac && !mac_geMountainLion ? "12px" : "18px";
481 this.horiz.style.minHeight = this.vert.style.minWidth = w; 466 this.horiz.style.height = this.vert.style.width = w;
482 var self = this; 467 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
483 var barMouseDown = function(e) { 468 this.disableHoriz = new Delayed;
484 if (e_target(e) != self.vert && e_target(e) != self.horiz) 469 this.disableVert = new Delayed;
485 operation(self.cm, onMouseDown)(e); 470 },
486 }; 471 enableZeroWidthBar: function(bar, delay) {
487 on(this.vert, "mousedown", barMouseDown); 472 bar.style.pointerEvents = "auto";
488 on(this.horiz, "mousedown", barMouseDown); 473 function maybeDisable() {
474 // To find out whether the scrollbar is still visible, we
475 // check whether the element under the pixel in the bottom
476 // left corner of the scrollbar box is the scrollbar box
477 // itself (when the bar is still visible) or its filler child
478 // (when the bar is hidden). If it is still visible, we keep
479 // it enabled, if it's hidden, we disable pointer events.
480 var box = bar.getBoundingClientRect();
481 var elt = document.elementFromPoint(box.left + 1, box.bottom - 1);
482 if (elt != bar) bar.style.pointerEvents = "none";
483 else delay.set(1000, maybeDisable);
484 }
485 delay.set(1000, maybeDisable);
489 }, 486 },
490 clear: function() { 487 clear: function() {
491 var parent = this.horiz.parentNode; 488 var parent = this.horiz.parentNode;
492 parent.removeChild(this.horiz); 489 parent.removeChild(this.horiz);
493 parent.removeChild(this.vert); 490 parent.removeChild(this.vert);
494 } 491 }
495 }, NativeScrollbars.prototype); 492 }, NativeScrollbars.prototype);
496 493
497 function NullScrollbars() {} 494 function NullScrollbars() {}
498 495
499 NullScrollbars.prototype = copyObj({ 496 NullScrollbars.prototype = copyObj({
500 update: function() { return {bottom: 0, right: 0}; }, 497 update: function() { return {bottom: 0, right: 0}; },
501 setScrollLeft: function() {}, 498 setScrollLeft: function() {},
502 setScrollTop: function() {}, 499 setScrollTop: function() {},
503 clear: function() {} 500 clear: function() {}
504 }, NullScrollbars.prototype); 501 }, NullScrollbars.prototype);
505 502
506 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbar s}; 503 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbar s};
507 504
508 function initScrollbars(cm) { 505 function initScrollbars(cm) {
509 if (cm.display.scrollbars) { 506 if (cm.display.scrollbars) {
510 cm.display.scrollbars.clear(); 507 cm.display.scrollbars.clear();
511 if (cm.display.scrollbars.addClass) 508 if (cm.display.scrollbars.addClass)
512 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); 509 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
513 } 510 }
514 511
515 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarSt yle](function(node) { 512 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarSt yle](function(node) {
516 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); 513 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
514 // Prevent clicks in the scrollbars from killing focus
517 on(node, "mousedown", function() { 515 on(node, "mousedown", function() {
518 if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); 516 if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
519 }); 517 });
520 node.setAttribute("not-content", "true"); 518 node.setAttribute("cm-not-content", "true");
521 }, function(pos, axis) { 519 }, function(pos, axis) {
522 if (axis == "horizontal") setScrollLeft(cm, pos); 520 if (axis == "horizontal") setScrollLeft(cm, pos);
523 else setScrollTop(cm, pos); 521 else setScrollTop(cm, pos);
524 }, cm); 522 }, cm);
525 if (cm.display.scrollbars.addClass) 523 if (cm.display.scrollbars.addClass)
526 addClass(cm.display.wrapper, cm.display.scrollbars.addClass); 524 addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
527 } 525 }
528 526
529 function updateScrollbars(cm, measure) { 527 function updateScrollbars(cm, measure) {
530 if (!measure) measure = measureForScrollbars(cm); 528 if (!measure) measure = measureForScrollbars(cm);
531 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; 529 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
532 updateScrollbarsInner(cm, measure); 530 updateScrollbarsInner(cm, measure);
533 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { 531 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
534 if (startWidth != cm.display.barWidth && cm.options.lineWrapping) 532 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
535 updateHeightsInViewport(cm); 533 updateHeightsInViewport(cm);
536 updateScrollbarsInner(cm, measureForScrollbars(cm)); 534 updateScrollbarsInner(cm, measureForScrollbars(cm));
537 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; 535 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
538 } 536 }
539 } 537 }
540 538
541 // Re-synchronize the fake scrollbars with the actual size of the 539 // Re-synchronize the fake scrollbars with the actual size of the
542 // content. 540 // content.
543 function updateScrollbarsInner(cm, measure) { 541 function updateScrollbarsInner(cm, measure) {
544 var d = cm.display; 542 var d = cm.display;
545 var sizes = d.scrollbars.update(measure); 543 var sizes = d.scrollbars.update(measure);
546 544
547 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; 545 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
548 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; 546 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
547 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
549 548
550 if (sizes.right && sizes.bottom) { 549 if (sizes.right && sizes.bottom) {
551 d.scrollbarFiller.style.display = "block"; 550 d.scrollbarFiller.style.display = "block";
552 d.scrollbarFiller.style.height = sizes.bottom + "px"; 551 d.scrollbarFiller.style.height = sizes.bottom + "px";
553 d.scrollbarFiller.style.width = sizes.right + "px"; 552 d.scrollbarFiller.style.width = sizes.right + "px";
554 } else d.scrollbarFiller.style.display = ""; 553 } else d.scrollbarFiller.style.display = "";
555 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixe dGutter) { 554 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixe dGutter) {
556 d.gutterFiller.style.display = "block"; 555 d.gutterFiller.style.display = "block";
557 d.gutterFiller.style.height = sizes.bottom + "px"; 556 d.gutterFiller.style.height = sizes.bottom + "px";
558 d.gutterFiller.style.width = measure.gutterWidth + "px"; 557 d.gutterFiller.style.width = measure.gutterWidth + "px";
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
607 // size for the current document size. Returns true when an update 606 // size for the current document size. Returns true when an update
608 // is needed. 607 // is needed.
609 function maybeUpdateLineNumberWidth(cm) { 608 function maybeUpdateLineNumberWidth(cm) {
610 if (!cm.options.lineNumbers) return false; 609 if (!cm.options.lineNumbers) return false;
611 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1) , display = cm.display; 610 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1) , display = cm.display;
612 if (last.length != display.lineNumChars) { 611 if (last.length != display.lineNumChars) {
613 var test = display.measure.appendChild(elt("div", [elt("div", last)], 612 var test = display.measure.appendChild(elt("div", [elt("div", last)],
614 "CodeMirror-linenumber CodeMirr or-gutter-elt")); 613 "CodeMirror-linenumber CodeMirr or-gutter-elt"));
615 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - inn erW; 614 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - inn erW;
616 display.lineGutter.style.width = ""; 615 display.lineGutter.style.width = "";
617 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidt h - padding); 616 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidt h - padding) + 1;
618 display.lineNumWidth = display.lineNumInnerWidth + padding; 617 display.lineNumWidth = display.lineNumInnerWidth + padding;
619 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; 618 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
620 display.lineGutter.style.width = display.lineNumWidth + "px"; 619 display.lineGutter.style.width = display.lineNumWidth + "px";
621 updateGutterSpace(cm); 620 updateGutterSpace(cm);
622 return true; 621 return true;
623 } 622 }
624 return false; 623 return false;
625 } 624 }
626 625
627 function lineNumberFor(options, i) { 626 function lineNumberFor(options, i) {
(...skipping 14 matching lines...) Expand all
642 641
643 this.viewport = viewport; 642 this.viewport = viewport;
644 // Store some values that we'll need later (but don't want to force a relayo ut for) 643 // Store some values that we'll need later (but don't want to force a relayo ut for)
645 this.visible = visibleLines(display, cm.doc, viewport); 644 this.visible = visibleLines(display, cm.doc, viewport);
646 this.editorIsHidden = !display.wrapper.offsetWidth; 645 this.editorIsHidden = !display.wrapper.offsetWidth;
647 this.wrapperHeight = display.wrapper.clientHeight; 646 this.wrapperHeight = display.wrapper.clientHeight;
648 this.wrapperWidth = display.wrapper.clientWidth; 647 this.wrapperWidth = display.wrapper.clientWidth;
649 this.oldDisplayWidth = displayWidth(cm); 648 this.oldDisplayWidth = displayWidth(cm);
650 this.force = force; 649 this.force = force;
651 this.dims = getDimensions(cm); 650 this.dims = getDimensions(cm);
651 this.events = [];
652 } 652 }
653 653
654 DisplayUpdate.prototype.signal = function(emitter, type) {
655 if (hasHandler(emitter, type))
656 this.events.push(arguments);
657 };
658 DisplayUpdate.prototype.finish = function() {
659 for (var i = 0; i < this.events.length; i++)
660 signal.apply(null, this.events[i]);
661 };
662
654 function maybeClipScrollbars(cm) { 663 function maybeClipScrollbars(cm) {
655 var display = cm.display; 664 var display = cm.display;
656 if (!display.scrollbarsClipped && display.scroller.offsetWidth) { 665 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
657 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.c lientWidth; 666 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.c lientWidth;
658 display.heightForcer.style.height = scrollGap(cm) + "px"; 667 display.heightForcer.style.height = scrollGap(cm) + "px";
659 display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; 668 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
660 display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; 669 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
661 display.scrollbarsClipped = true; 670 display.scrollbarsClipped = true;
662 } 671 }
663 } 672 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
717 if (toUpdate > 4) display.lineDiv.style.display = ""; 726 if (toUpdate > 4) display.lineDiv.style.display = "";
718 display.renderedView = display.view; 727 display.renderedView = display.view;
719 // There might have been a widget with a focused element that got 728 // There might have been a widget with a focused element that got
720 // hidden or updated, if so re-focus it. 729 // hidden or updated, if so re-focus it.
721 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus (); 730 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus ();
722 731
723 // Prevent selection and cursors from interfering with the scroll 732 // Prevent selection and cursors from interfering with the scroll
724 // width and height. 733 // width and height.
725 removeChildren(display.cursorDiv); 734 removeChildren(display.cursorDiv);
726 removeChildren(display.selectionDiv); 735 removeChildren(display.selectionDiv);
727 display.gutters.style.height = 0; 736 display.gutters.style.height = display.sizer.style.minHeight = 0;
728 737
729 if (different) { 738 if (different) {
730 display.lastWrapHeight = update.wrapperHeight; 739 display.lastWrapHeight = update.wrapperHeight;
731 display.lastWrapWidth = update.wrapperWidth; 740 display.lastWrapWidth = update.wrapperWidth;
732 startWorker(cm, 400); 741 startWorker(cm, 400);
733 } 742 }
734 743
735 display.updateLineNumbers = null; 744 display.updateLineNumbers = null;
736 745
737 return true; 746 return true;
738 } 747 }
739 748
740 function postUpdateDisplay(cm, update) { 749 function postUpdateDisplay(cm, update) {
741 var force = update.force, viewport = update.viewport; 750 var viewport = update.viewport;
751
742 for (var first = true;; first = false) { 752 for (var first = true;; first = false) {
743 if (first && cm.options.lineWrapping && update.oldDisplayWidth != displayW idth(cm)) { 753 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displa yWidth(cm)) {
744 force = true;
745 } else {
746 force = false;
747 // Clip forced viewport to actual scrollable area. 754 // Clip forced viewport to actual scrollable area.
748 if (viewport && viewport.top != null) 755 if (viewport && viewport.top != null)
749 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - di splayHeight(cm), viewport.top)}; 756 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - di splayHeight(cm), viewport.top)};
750 // Updated line heights might result in the drawn area not 757 // Updated line heights might result in the drawn area not
751 // actually covering the viewport. Keep looping until it does. 758 // actually covering the viewport. Keep looping until it does.
752 update.visible = visibleLines(cm.display, cm.doc, viewport); 759 update.visible = visibleLines(cm.display, cm.doc, viewport);
753 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= c m.display.viewTo) 760 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= c m.display.viewTo)
754 break; 761 break;
755 } 762 }
756 if (!updateDisplayIfNeeded(cm, update)) break; 763 if (!updateDisplayIfNeeded(cm, update)) break;
757 updateHeightsInViewport(cm); 764 updateHeightsInViewport(cm);
758 var barMeasure = measureForScrollbars(cm); 765 var barMeasure = measureForScrollbars(cm);
759 updateSelection(cm); 766 updateSelection(cm);
767 updateScrollbars(cm, barMeasure);
760 setDocumentHeight(cm, barMeasure); 768 setDocumentHeight(cm, barMeasure);
761 updateScrollbars(cm, barMeasure);
762 } 769 }
763 770
764 signalLater(cm, "update", cm); 771 update.signal(cm, "update", cm);
765 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { 772 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
766 signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.view To); 773 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.vi ewTo);
767 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedView To = cm.display.viewTo; 774 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedView To = cm.display.viewTo;
768 } 775 }
769 } 776 }
770 777
771 function updateDisplaySimple(cm, viewport) { 778 function updateDisplaySimple(cm, viewport) {
772 var update = new DisplayUpdate(cm, viewport); 779 var update = new DisplayUpdate(cm, viewport);
773 if (updateDisplayIfNeeded(cm, update)) { 780 if (updateDisplayIfNeeded(cm, update)) {
774 updateHeightsInViewport(cm); 781 updateHeightsInViewport(cm);
775 postUpdateDisplay(cm, update); 782 postUpdateDisplay(cm, update);
776 var barMeasure = measureForScrollbars(cm); 783 var barMeasure = measureForScrollbars(cm);
777 updateSelection(cm); 784 updateSelection(cm);
785 updateScrollbars(cm, barMeasure);
778 setDocumentHeight(cm, barMeasure); 786 setDocumentHeight(cm, barMeasure);
779 updateScrollbars(cm, barMeasure); 787 update.finish();
780 } 788 }
781 } 789 }
782 790
783 function setDocumentHeight(cm, measure) { 791 function setDocumentHeight(cm, measure) {
784 cm.display.sizer.style.minHeight = measure.docHeight + "px"; 792 cm.display.sizer.style.minHeight = measure.docHeight + "px";
785 var total = measure.docHeight + cm.display.barHeight; 793 cm.display.heightForcer.style.top = measure.docHeight + "px";
786 cm.display.heightForcer.style.top = total + "px"; 794 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
787 cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.cl ientHeight) + "px";
788 } 795 }
789 796
790 // Read the actual heights of the rendered lines, and update their 797 // Read the actual heights of the rendered lines, and update their
791 // stored heights to match. 798 // stored heights to match.
792 function updateHeightsInViewport(cm) { 799 function updateHeightsInViewport(cm) {
793 var display = cm.display; 800 var display = cm.display;
794 var prevBottom = display.lineDiv.offsetTop; 801 var prevBottom = display.lineDiv.offsetTop;
795 for (var i = 0; i < display.view.length; i++) { 802 for (var i = 0; i < display.view.length; i++) {
796 var cur = display.view[i], height; 803 var cur = display.view[i], height;
797 if (cur.hidden) continue; 804 if (cur.hidden) continue;
(...skipping 13 matching lines...) Expand all
811 if (cur.rest) for (var j = 0; j < cur.rest.length; j++) 818 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
812 updateWidgetHeight(cur.rest[j]); 819 updateWidgetHeight(cur.rest[j]);
813 } 820 }
814 } 821 }
815 } 822 }
816 823
817 // Read and store the height of line widgets associated with the 824 // Read and store the height of line widgets associated with the
818 // given line. 825 // given line.
819 function updateWidgetHeight(line) { 826 function updateWidgetHeight(line) {
820 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i) 827 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
821 line.widgets[i].height = line.widgets[i].node.offsetHeight; 828 line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight;
822 } 829 }
823 830
824 // Do a bulk-read of the DOM positions and sizes needed to draw the 831 // Do a bulk-read of the DOM positions and sizes needed to draw the
825 // view, so that we don't interleave reading and writing to the DOM. 832 // view, so that we don't interleave reading and writing to the DOM.
826 function getDimensions(cm) { 833 function getDimensions(cm) {
827 var d = cm.display, left = {}, width = {}; 834 var d = cm.display, left = {}, width = {};
828 var gutterLeft = d.gutters.clientLeft; 835 var gutterLeft = d.gutters.clientLeft;
829 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { 836 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
830 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; 837 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
831 width[cm.options.gutters[i]] = n.clientWidth; 838 width[cm.options.gutters[i]] = n.clientWidth;
(...skipping 22 matching lines...) Expand all
854 node.parentNode.removeChild(node); 861 node.parentNode.removeChild(node);
855 return next; 862 return next;
856 } 863 }
857 864
858 var view = display.view, lineN = display.viewFrom; 865 var view = display.view, lineN = display.viewFrom;
859 // Loop over the elements in the view, syncing cur (the DOM nodes 866 // Loop over the elements in the view, syncing cur (the DOM nodes
860 // in display.lineDiv) with the view as we go. 867 // in display.lineDiv) with the view as we go.
861 for (var i = 0; i < view.length; i++) { 868 for (var i = 0; i < view.length; i++) {
862 var lineView = view[i]; 869 var lineView = view[i];
863 if (lineView.hidden) { 870 if (lineView.hidden) {
864 } else if (!lineView.node) { // Not drawn yet 871 } else if (!lineView.node || lineView.node.parentNode != container) { // N ot drawn yet
865 var node = buildLineElement(cm, lineView, lineN, dims); 872 var node = buildLineElement(cm, lineView, lineN, dims);
866 container.insertBefore(node, cur); 873 container.insertBefore(node, cur);
867 } else { // Already drawn 874 } else { // Already drawn
868 while (cur != lineView.node) cur = rm(cur); 875 while (cur != lineView.node) cur = rm(cur);
869 var updateNumber = lineNumbers && updateNumbersFrom != null && 876 var updateNumber = lineNumbers && updateNumbersFrom != null &&
870 updateNumbersFrom <= lineN && lineView.lineNumber; 877 updateNumbersFrom <= lineN && lineView.lineNumber;
871 if (lineView.changes) { 878 if (lineView.changes) {
872 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false; 879 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
873 updateLineForChanges(cm, lineView, lineN, dims); 880 updateLineForChanges(cm, lineView, lineN, dims);
874 } 881 }
(...skipping 10 matching lines...) Expand all
885 892
886 // When an aspect of a line changes, a string is added to 893 // When an aspect of a line changes, a string is added to
887 // lineView.changes. This updates the relevant part of the line's 894 // lineView.changes. This updates the relevant part of the line's
888 // DOM structure. 895 // DOM structure.
889 function updateLineForChanges(cm, lineView, lineN, dims) { 896 function updateLineForChanges(cm, lineView, lineN, dims) {
890 for (var j = 0; j < lineView.changes.length; j++) { 897 for (var j = 0; j < lineView.changes.length; j++) {
891 var type = lineView.changes[j]; 898 var type = lineView.changes[j];
892 if (type == "text") updateLineText(cm, lineView); 899 if (type == "text") updateLineText(cm, lineView);
893 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims); 900 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
894 else if (type == "class") updateLineClasses(lineView); 901 else if (type == "class") updateLineClasses(lineView);
895 else if (type == "widget") updateLineWidgets(lineView, dims); 902 else if (type == "widget") updateLineWidgets(cm, lineView, dims);
896 } 903 }
897 lineView.changes = null; 904 lineView.changes = null;
898 } 905 }
899 906
900 // Lines with gutter elements, widgets or a background class need to 907 // Lines with gutter elements, widgets or a background class need to
901 // be wrapped, and have the extra elements added to the wrapper div 908 // be wrapped, and have the extra elements added to the wrapper div
902 function ensureLineWrapped(lineView) { 909 function ensureLineWrapped(lineView) {
903 if (lineView.node == lineView.text) { 910 if (lineView.node == lineView.text) {
904 lineView.node = elt("div", null, null, "position: relative"); 911 lineView.node = elt("div", null, null, "position: relative");
905 if (lineView.text.parentNode) 912 if (lineView.text.parentNode)
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
960 lineView.node.className = ""; 967 lineView.node.className = "";
961 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.li ne.textClass || "") : lineView.line.textClass; 968 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.li ne.textClass || "") : lineView.line.textClass;
962 lineView.text.className = textClass || ""; 969 lineView.text.className = textClass || "";
963 } 970 }
964 971
965 function updateLineGutter(cm, lineView, lineN, dims) { 972 function updateLineGutter(cm, lineView, lineN, dims) {
966 if (lineView.gutter) { 973 if (lineView.gutter) {
967 lineView.node.removeChild(lineView.gutter); 974 lineView.node.removeChild(lineView.gutter);
968 lineView.gutter = null; 975 lineView.gutter = null;
969 } 976 }
977 if (lineView.gutterBackground) {
978 lineView.node.removeChild(lineView.gutterBackground);
979 lineView.gutterBackground = null;
980 }
981 if (lineView.line.gutterClass) {
982 var wrap = ensureLineWrapped(lineView);
983 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
984 "left: " + (cm.options.fixedGutter ? dims. fixedPos : -dims.gutterTotalWidth) +
985 "px; width: " + dims.gutterTotalWidth + "p x");
986 wrap.insertBefore(lineView.gutterBackground, lineView.text);
987 }
970 var markers = lineView.line.gutterMarkers; 988 var markers = lineView.line.gutterMarkers;
971 if (cm.options.lineNumbers || markers) { 989 if (cm.options.lineNumbers || markers) {
972 var wrap = ensureLineWrapped(lineView); 990 var wrap = ensureLineWrapped(lineView);
973 var gutterWrap = lineView.gutter = 991 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wra pper", "left: " +
974 wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "left: " + 992 (cm.options.fixedGutter ? dims.fixe dPos : -dims.gutterTotalWidth) + "px");
975 (cm.options.fixedGutter ? dims.fixedPos : -dims.gu tterTotalWidth) + 993 cm.display.input.setUneditable(gutterWrap);
976 "px; width: " + dims.gutterTotalWidth + "px"), 994 wrap.insertBefore(gutterWrap, lineView.text);
977 lineView.text);
978 if (lineView.line.gutterClass) 995 if (lineView.line.gutterClass)
979 gutterWrap.className += " " + lineView.line.gutterClass; 996 gutterWrap.className += " " + lineView.line.gutterClass;
980 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumber s"])) 997 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumber s"]))
981 lineView.lineNumber = gutterWrap.appendChild( 998 lineView.lineNumber = gutterWrap.appendChild(
982 elt("div", lineNumberFor(cm.options, lineN), 999 elt("div", lineNumberFor(cm.options, lineN),
983 "CodeMirror-linenumber CodeMirror-gutter-elt", 1000 "CodeMirror-linenumber CodeMirror-gutter-elt",
984 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " 1001 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
985 + cm.display.lineNumInnerWidth + "px")); 1002 + cm.display.lineNumInnerWidth + "px"));
986 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) { 1003 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
987 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && ma rkers[id]; 1004 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && ma rkers[id];
988 if (found) 1005 if (found)
989 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "l eft: " + 1006 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "l eft: " +
990 dims.gutterLeft[id] + "px; width: " + dims. gutterWidth[id] + "px")); 1007 dims.gutterLeft[id] + "px; width: " + dims. gutterWidth[id] + "px"));
991 } 1008 }
992 } 1009 }
993 } 1010 }
994 1011
995 function updateLineWidgets(lineView, dims) { 1012 function updateLineWidgets(cm, lineView, dims) {
996 if (lineView.alignable) lineView.alignable = null; 1013 if (lineView.alignable) lineView.alignable = null;
997 for (var node = lineView.node.firstChild, next; node; node = next) { 1014 for (var node = lineView.node.firstChild, next; node; node = next) {
998 var next = node.nextSibling; 1015 var next = node.nextSibling;
999 if (node.className == "CodeMirror-linewidget") 1016 if (node.className == "CodeMirror-linewidget")
1000 lineView.node.removeChild(node); 1017 lineView.node.removeChild(node);
1001 } 1018 }
1002 insertLineWidgets(lineView, dims); 1019 insertLineWidgets(cm, lineView, dims);
1003 } 1020 }
1004 1021
1005 // Build a line's DOM representation from scratch 1022 // Build a line's DOM representation from scratch
1006 function buildLineElement(cm, lineView, lineN, dims) { 1023 function buildLineElement(cm, lineView, lineN, dims) {
1007 var built = getLineContent(cm, lineView); 1024 var built = getLineContent(cm, lineView);
1008 lineView.text = lineView.node = built.pre; 1025 lineView.text = lineView.node = built.pre;
1009 if (built.bgClass) lineView.bgClass = built.bgClass; 1026 if (built.bgClass) lineView.bgClass = built.bgClass;
1010 if (built.textClass) lineView.textClass = built.textClass; 1027 if (built.textClass) lineView.textClass = built.textClass;
1011 1028
1012 updateLineClasses(lineView); 1029 updateLineClasses(lineView);
1013 updateLineGutter(cm, lineView, lineN, dims); 1030 updateLineGutter(cm, lineView, lineN, dims);
1014 insertLineWidgets(lineView, dims); 1031 insertLineWidgets(cm, lineView, dims);
1015 return lineView.node; 1032 return lineView.node;
1016 } 1033 }
1017 1034
1018 // A lineView may contain multiple logical lines (when merged by 1035 // A lineView may contain multiple logical lines (when merged by
1019 // collapsed spans). The widgets for all of them need to be drawn. 1036 // collapsed spans). The widgets for all of them need to be drawn.
1020 function insertLineWidgets(lineView, dims) { 1037 function insertLineWidgets(cm, lineView, dims) {
1021 insertLineWidgetsFor(lineView.line, lineView, dims, true); 1038 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
1022 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) 1039 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1023 insertLineWidgetsFor(lineView.rest[i], lineView, dims, false); 1040 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
1024 } 1041 }
1025 1042
1026 function insertLineWidgetsFor(line, lineView, dims, allowAbove) { 1043 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
1027 if (!line.widgets) return; 1044 if (!line.widgets) return;
1028 var wrap = ensureLineWrapped(lineView); 1045 var wrap = ensureLineWrapped(lineView);
1029 for (var i = 0, ws = line.widgets; i < ws.length; ++i) { 1046 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
1030 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidge t"); 1047 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidge t");
1031 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true "); 1048 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true ");
1032 positionLineWidget(widget, node, lineView, dims); 1049 positionLineWidget(widget, node, lineView, dims);
1050 cm.display.input.setUneditable(node);
1033 if (allowAbove && widget.above) 1051 if (allowAbove && widget.above)
1034 wrap.insertBefore(node, lineView.gutter || lineView.text); 1052 wrap.insertBefore(node, lineView.gutter || lineView.text);
1035 else 1053 else
1036 wrap.appendChild(node); 1054 wrap.appendChild(node);
1037 signalLater(widget, "redraw"); 1055 signalLater(widget, "redraw");
1038 } 1056 }
1039 } 1057 }
1040 1058
1041 function positionLineWidget(widget, node, lineView, dims) { 1059 function positionLineWidget(widget, node, lineView, dims) {
1042 if (widget.noHScroll) { 1060 if (widget.noHScroll) {
(...skipping 22 matching lines...) Expand all
1065 }; 1083 };
1066 1084
1067 // Compare two positions, return 0 if they are the same, a negative 1085 // Compare two positions, return 0 if they are the same, a negative
1068 // number when a is less, and a positive number otherwise. 1086 // number when a is less, and a positive number otherwise.
1069 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; }; 1087 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
1070 1088
1071 function copyPos(x) {return Pos(x.line, x.ch);} 1089 function copyPos(x) {return Pos(x.line, x.ch);}
1072 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; } 1090 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
1073 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; } 1091 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
1074 1092
1093 // INPUT HANDLING
1094
1095 function ensureFocus(cm) {
1096 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
1097 }
1098
1099 // This will be set to a {lineWise: bool, text: [string]} object, so
1100 // that, when pasting, we know what kind of selections the copied
1101 // text was made out of.
1102 var lastCopied = null;
1103
1104 function applyTextInput(cm, inserted, deleted, sel, origin) {
1105 var doc = cm.doc;
1106 cm.display.shift = false;
1107 if (!sel) sel = doc.sel;
1108
1109 var paste = cm.state.pasteIncoming || origin == "paste";
1110 var textLines = doc.splitLines(inserted), multiPaste = null
1111 // When pasing N lines into N selections, insert one line per selection
1112 if (paste && sel.ranges.length > 1) {
1113 if (lastCopied && lastCopied.text.join("\n") == inserted) {
1114 if (sel.ranges.length % lastCopied.text.length == 0) {
1115 multiPaste = [];
1116 for (var i = 0; i < lastCopied.text.length; i++)
1117 multiPaste.push(doc.splitLines(lastCopied.text[i]));
1118 }
1119 } else if (textLines.length == sel.ranges.length) {
1120 multiPaste = map(textLines, function(l) { return [l]; });
1121 }
1122 }
1123
1124 // Normal behavior is to insert the new text into every selection
1125 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1126 var range = sel.ranges[i];
1127 var from = range.from(), to = range.to();
1128 if (range.empty()) {
1129 if (deleted && deleted > 0) // Handle deletion
1130 from = Pos(from.line, from.ch - deleted);
1131 else if (cm.state.overwrite && !paste) // Handle overwrite
1132 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
1133 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
1134 from = to = Pos(from.line, 0)
1135 }
1136 var updateInput = cm.curOp.updateInput;
1137 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % m ultiPaste.length] : textLines,
1138 origin: origin || (paste ? "paste" : cm.state.cutIncomi ng ? "cut" : "+input")};
1139 makeChange(cm.doc, changeEvent);
1140 signalLater(cm, "inputRead", cm, changeEvent);
1141 }
1142 if (inserted && !paste)
1143 triggerElectric(cm, inserted);
1144
1145 ensureCursorVisible(cm);
1146 cm.curOp.updateInput = updateInput;
1147 cm.curOp.typing = true;
1148 cm.state.pasteIncoming = cm.state.cutIncoming = false;
1149 }
1150
1151 function handlePaste(e, cm) {
1152 var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
1153 if (pasted) {
1154 e.preventDefault();
1155 if (!cm.isReadOnly() && !cm.options.disableInput)
1156 runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); } );
1157 return true;
1158 }
1159 }
1160
1161 function triggerElectric(cm, inserted) {
1162 // When an 'electric' character is inserted, immediately trigger a reindent
1163 if (!cm.options.electricChars || !cm.options.smartIndent) return;
1164 var sel = cm.doc.sel;
1165
1166 for (var i = sel.ranges.length - 1; i >= 0; i--) {
1167 var range = sel.ranges[i];
1168 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head .line)) continue;
1169 var mode = cm.getModeAt(range.head);
1170 var indented = false;
1171 if (mode.electricChars) {
1172 for (var j = 0; j < mode.electricChars.length; j++)
1173 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
1174 indented = indentLine(cm, range.head.line, "smart");
1175 break;
1176 }
1177 } else if (mode.electricInput) {
1178 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice( 0, range.head.ch)))
1179 indented = indentLine(cm, range.head.line, "smart");
1180 }
1181 if (indented) signalLater(cm, "electricInput", cm, range.head.line);
1182 }
1183 }
1184
1185 function copyableRanges(cm) {
1186 var text = [], ranges = [];
1187 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
1188 var line = cm.doc.sel.ranges[i].head.line;
1189 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
1190 ranges.push(lineRange);
1191 text.push(cm.getRange(lineRange.anchor, lineRange.head));
1192 }
1193 return {text: text, ranges: ranges};
1194 }
1195
1196 function disableBrowserMagic(field) {
1197 field.setAttribute("autocorrect", "off");
1198 field.setAttribute("autocapitalize", "off");
1199 field.setAttribute("spellcheck", "false");
1200 }
1201
1202 // TEXTAREA INPUT STYLE
1203
1204 function TextareaInput(cm) {
1205 this.cm = cm;
1206 // See input.poll and input.reset
1207 this.prevInput = "";
1208
1209 // Flag that indicates whether we expect input to appear real soon
1210 // now (after some event like 'keypress' or 'input') and are
1211 // polling intensively.
1212 this.pollingFast = false;
1213 // Self-resetting timeout for the poller
1214 this.polling = new Delayed();
1215 // Tracks when input.reset has punted to just putting a short
1216 // string into the textarea instead of the full selection.
1217 this.inaccurateSelection = false;
1218 // Used to work around IE issue with selection being forgotten when focus mo ves away from textarea
1219 this.hasSelection = false;
1220 this.composing = null;
1221 };
1222
1223 function hiddenTextarea() {
1224 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padd ing: 0; width: 1px; height: 1em; outline: none");
1225 var div = elt("div", [te], null, "overflow: hidden; position: relative; widt h: 3px; height: 0px;");
1226 // The textarea is kept positioned near the cursor to prevent the
1227 // fact that it'll be scrolled into view on input from scrolling
1228 // our fake cursor out of view. On webkit, when wrap=off, paste is
1229 // very slow. So make the area wide instead.
1230 if (webkit) te.style.width = "1000px";
1231 else te.setAttribute("wrap", "off");
1232 // If border: 0; -- iOS fails to open keyboard (issue #1287)
1233 if (ios) te.style.border = "1px solid black";
1234 disableBrowserMagic(te);
1235 return div;
1236 }
1237
1238 TextareaInput.prototype = copyObj({
1239 init: function(display) {
1240 var input = this, cm = this.cm;
1241
1242 // Wraps and hides input textarea
1243 var div = this.wrapper = hiddenTextarea();
1244 // The semihidden textarea that is focused when the editor is
1245 // focused, and receives input.
1246 var te = this.textarea = div.firstChild;
1247 display.wrapper.insertBefore(div, display.wrapper.firstChild);
1248
1249 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
1250 if (ios) te.style.width = "0px";
1251
1252 on(te, "input", function() {
1253 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = nu ll;
1254 input.poll();
1255 });
1256
1257 on(te, "paste", function(e) {
1258 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return
1259
1260 cm.state.pasteIncoming = true;
1261 input.fastPoll();
1262 });
1263
1264 function prepareCopyCut(e) {
1265 if (signalDOMEvent(cm, e)) return
1266 if (cm.somethingSelected()) {
1267 lastCopied = {lineWise: false, text: cm.getSelections()};
1268 if (input.inaccurateSelection) {
1269 input.prevInput = "";
1270 input.inaccurateSelection = false;
1271 te.value = lastCopied.text.join("\n");
1272 selectInput(te);
1273 }
1274 } else if (!cm.options.lineWiseCopyCut) {
1275 return;
1276 } else {
1277 var ranges = copyableRanges(cm);
1278 lastCopied = {lineWise: true, text: ranges.text};
1279 if (e.type == "cut") {
1280 cm.setSelections(ranges.ranges, null, sel_dontScroll);
1281 } else {
1282 input.prevInput = "";
1283 te.value = ranges.text.join("\n");
1284 selectInput(te);
1285 }
1286 }
1287 if (e.type == "cut") cm.state.cutIncoming = true;
1288 }
1289 on(te, "cut", prepareCopyCut);
1290 on(te, "copy", prepareCopyCut);
1291
1292 on(display.scroller, "paste", function(e) {
1293 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return;
1294 cm.state.pasteIncoming = true;
1295 input.focus();
1296 });
1297
1298 // Prevent normal selection in the editor (we handle our own)
1299 on(display.lineSpace, "selectstart", function(e) {
1300 if (!eventInWidget(display, e)) e_preventDefault(e);
1301 });
1302
1303 on(te, "compositionstart", function() {
1304 var start = cm.getCursor("from");
1305 if (input.composing) input.composing.range.clear()
1306 input.composing = {
1307 start: start,
1308 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror- composing"})
1309 };
1310 });
1311 on(te, "compositionend", function() {
1312 if (input.composing) {
1313 input.poll();
1314 input.composing.range.clear();
1315 input.composing = null;
1316 }
1317 });
1318 },
1319
1320 prepareSelection: function() {
1321 // Redraw the selection and/or cursor
1322 var cm = this.cm, display = cm.display, doc = cm.doc;
1323 var result = prepareSelection(cm);
1324
1325 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1326 if (cm.options.moveInputWithCursor) {
1327 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1328 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display .lineDiv.getBoundingClientRect();
1329 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1330 headPos.top + lineOff.top - wrapOff. top));
1331 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1332 headPos.left + lineOff.left - wrapO ff.left));
1333 }
1334
1335 return result;
1336 },
1337
1338 showSelection: function(drawn) {
1339 var cm = this.cm, display = cm.display;
1340 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
1341 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
1342 if (drawn.teTop != null) {
1343 this.wrapper.style.top = drawn.teTop + "px";
1344 this.wrapper.style.left = drawn.teLeft + "px";
1345 }
1346 },
1347
1348 // Reset the input to correspond to the selection (or to be empty,
1349 // when not typing and nothing is selected)
1350 reset: function(typing) {
1351 if (this.contextMenuPending) return;
1352 var minimal, selected, cm = this.cm, doc = cm.doc;
1353 if (cm.somethingSelected()) {
1354 this.prevInput = "";
1355 var range = doc.sel.primary();
1356 minimal = hasCopyEvent &&
1357 (range.to().line - range.from().line > 100 || (selected = cm.getSelect ion()).length > 1000);
1358 var content = minimal ? "-" : selected || cm.getSelection();
1359 this.textarea.value = content;
1360 if (cm.state.focused) selectInput(this.textarea);
1361 if (ie && ie_version >= 9) this.hasSelection = content;
1362 } else if (!typing) {
1363 this.prevInput = this.textarea.value = "";
1364 if (ie && ie_version >= 9) this.hasSelection = null;
1365 }
1366 this.inaccurateSelection = minimal;
1367 },
1368
1369 getField: function() { return this.textarea; },
1370
1371 supportsTouch: function() { return false; },
1372
1373 focus: function() {
1374 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != t his.textarea)) {
1375 try { this.textarea.focus(); }
1376 catch (e) {} // IE8 will throw if the textarea is display: none or not i n DOM
1377 }
1378 },
1379
1380 blur: function() { this.textarea.blur(); },
1381
1382 resetPosition: function() {
1383 this.wrapper.style.top = this.wrapper.style.left = 0;
1384 },
1385
1386 receivedFocus: function() { this.slowPoll(); },
1387
1388 // Poll for input changes, using the normal rate of polling. This
1389 // runs as long as the editor is focused.
1390 slowPoll: function() {
1391 var input = this;
1392 if (input.pollingFast) return;
1393 input.polling.set(this.cm.options.pollInterval, function() {
1394 input.poll();
1395 if (input.cm.state.focused) input.slowPoll();
1396 });
1397 },
1398
1399 // When an event has just come in that is likely to add or change
1400 // something in the input textarea, we poll faster, to ensure that
1401 // the change appears on the screen quickly.
1402 fastPoll: function() {
1403 var missed = false, input = this;
1404 input.pollingFast = true;
1405 function p() {
1406 var changed = input.poll();
1407 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
1408 else {input.pollingFast = false; input.slowPoll();}
1409 }
1410 input.polling.set(20, p);
1411 },
1412
1413 // Read input from the textarea, and update the document to match.
1414 // When something is selected, it is present in the textarea, and
1415 // selected (unless it is huge, in which case a placeholder is
1416 // used). When nothing is selected, the cursor sits after previously
1417 // seen text (can be empty), which is stored in prevInput (we must
1418 // not reset the textarea when typing, because that breaks IME).
1419 poll: function() {
1420 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
1421 // Since this is called a *lot*, try to bail out as cheaply as
1422 // possible when it is clear that nothing happened. hasSelection
1423 // will be the case when there is a lot of text in the textarea,
1424 // in which case reading its value would be expensive.
1425 if (this.contextMenuPending || !cm.state.focused ||
1426 (hasSelection(input) && !prevInput && !this.composing) ||
1427 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
1428 return false;
1429
1430 var text = input.value;
1431 // If nothing changed, bail.
1432 if (text == prevInput && !cm.somethingSelected()) return false;
1433 // Work around nonsensical selection resetting in IE9/10, and
1434 // inexplicable appearance of private area unicode characters on
1435 // some key combos in Mac (#2689).
1436 if (ie && ie_version >= 9 && this.hasSelection === text ||
1437 mac && /[\uf700-\uf7ff]/.test(text)) {
1438 cm.display.input.reset();
1439 return false;
1440 }
1441
1442 if (cm.doc.sel == cm.display.selForContextMenu) {
1443 var first = text.charCodeAt(0);
1444 if (first == 0x200b && !prevInput) prevInput = "\u200b";
1445 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
1446 }
1447 // Find the part of the input that is actually new
1448 var same = 0, l = Math.min(prevInput.length, text.length);
1449 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++ same;
1450
1451 var self = this;
1452 runInOp(cm, function() {
1453 applyTextInput(cm, text.slice(same), prevInput.length - same,
1454 null, self.composing ? "*compose" : null);
1455
1456 // Don't leave long text in the textarea, since it makes further polling slow
1457 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.pr evInput = "";
1458 else self.prevInput = text;
1459
1460 if (self.composing) {
1461 self.composing.range.clear();
1462 self.composing.range = cm.markText(self.composing.start, cm.getCursor( "to"),
1463 {className: "CodeMirror-composing"} );
1464 }
1465 });
1466 return true;
1467 },
1468
1469 ensurePolled: function() {
1470 if (this.pollingFast && this.poll()) this.pollingFast = false;
1471 },
1472
1473 onKeyPress: function() {
1474 if (ie && ie_version >= 9) this.hasSelection = null;
1475 this.fastPoll();
1476 },
1477
1478 onContextMenu: function(e) {
1479 var input = this, cm = input.cm, display = cm.display, te = input.textarea ;
1480 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
1481 if (!pos || presto) return; // Opera is difficult.
1482
1483 // Reset the current text selection only if the click is done outside of t he selection
1484 // and 'resetSelectionOnContextMenu' option is true.
1485 var reset = cm.options.resetSelectionOnContextMenu;
1486 if (reset && cm.doc.sel.contains(pos) == -1)
1487 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll );
1488
1489 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText ;
1490 input.wrapper.style.cssText = "position: absolute"
1491 var wrapperBox = input.wrapper.getBoundingClientRect()
1492 te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) +
1493 "px; left: " + (e.clientX - wrapperBox.left - 5) + "px; z-index: 1000; b ackground: " +
1494 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
1495 "; outline: none; border-width: 0; outline: none; overflow: hidden; opac ity: .05; filter: alpha(opacity=5);";
1496 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue ( #2712)
1497 display.input.focus();
1498 if (webkit) window.scrollTo(null, oldScrollY);
1499 display.input.reset();
1500 // Adds "Select all" to context menu in FF
1501 if (!cm.somethingSelected()) te.value = input.prevInput = " ";
1502 input.contextMenuPending = true;
1503 display.selForContextMenu = cm.doc.sel;
1504 clearTimeout(display.detectingSelectAll);
1505
1506 // Select-all will be greyed out if there's nothing to select, so
1507 // this adds a zero-width space so that we can later check whether
1508 // it got selected.
1509 function prepareSelectAllHack() {
1510 if (te.selectionStart != null) {
1511 var selected = cm.somethingSelected();
1512 var extval = "\u200b" + (selected ? te.value : "");
1513 te.value = "\u21da"; // Used to catch context-menu undo
1514 te.value = extval;
1515 input.prevInput = selected ? "" : "\u200b";
1516 te.selectionStart = 1; te.selectionEnd = extval.length;
1517 // Re-set this, in case some other handler touched the
1518 // selection in the meantime.
1519 display.selForContextMenu = cm.doc.sel;
1520 }
1521 }
1522 function rehide() {
1523 input.contextMenuPending = false;
1524 input.wrapper.style.cssText = oldWrapperCSS
1525 te.style.cssText = oldCSS;
1526 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroll er.scrollTop = scrollPos);
1527
1528 // Try to detect the user choosing select-all
1529 if (te.selectionStart != null) {
1530 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
1531 var i = 0, poll = function() {
1532 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
1533 te.selectionEnd > 0 && input.prevInput == "\u200b")
1534 operation(cm, commands.selectAll)(cm);
1535 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500 );
1536 else display.input.reset();
1537 };
1538 display.detectingSelectAll = setTimeout(poll, 200);
1539 }
1540 }
1541
1542 if (ie && ie_version >= 9) prepareSelectAllHack();
1543 if (captureRightClick) {
1544 e_stop(e);
1545 var mouseup = function() {
1546 off(window, "mouseup", mouseup);
1547 setTimeout(rehide, 20);
1548 };
1549 on(window, "mouseup", mouseup);
1550 } else {
1551 setTimeout(rehide, 50);
1552 }
1553 },
1554
1555 readOnlyChanged: function(val) {
1556 if (!val) this.reset();
1557 },
1558
1559 setUneditable: nothing,
1560
1561 needsContentAttribute: false
1562 }, TextareaInput.prototype);
1563
1564 // CONTENTEDITABLE INPUT STYLE
1565
1566 function ContentEditableInput(cm) {
1567 this.cm = cm;
1568 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.last FocusOffset = null;
1569 this.polling = new Delayed();
1570 this.gracePeriod = false;
1571 }
1572
1573 ContentEditableInput.prototype = copyObj({
1574 init: function(display) {
1575 var input = this, cm = input.cm;
1576 var div = input.div = display.lineDiv;
1577 disableBrowserMagic(div);
1578
1579 on(div, "paste", function(e) {
1580 if (!signalDOMEvent(cm, e)) handlePaste(e, cm);
1581 })
1582
1583 on(div, "compositionstart", function(e) {
1584 var data = e.data;
1585 input.composing = {sel: cm.doc.sel, data: data, startData: data};
1586 if (!data) return;
1587 var prim = cm.doc.sel.primary();
1588 var line = cm.getLine(prim.head.line);
1589 var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
1590 if (found > -1 && found <= prim.head.ch)
1591 input.composing.sel = simpleSelection(Pos(prim.head.line, found),
1592 Pos(prim.head.line, found + data .length));
1593 });
1594 on(div, "compositionupdate", function(e) {
1595 input.composing.data = e.data;
1596 });
1597 on(div, "compositionend", function(e) {
1598 var ours = input.composing;
1599 if (!ours) return;
1600 if (e.data != ours.startData && !/\u200b/.test(e.data))
1601 ours.data = e.data;
1602 // Need a small delay to prevent other code (input event,
1603 // selection polling) from doing damage when fired right after
1604 // compositionend.
1605 setTimeout(function() {
1606 if (!ours.handled)
1607 input.applyComposition(ours);
1608 if (input.composing == ours)
1609 input.composing = null;
1610 }, 50);
1611 });
1612
1613 on(div, "touchstart", function() {
1614 input.forceCompositionEnd();
1615 });
1616
1617 on(div, "input", function() {
1618 if (input.composing) return;
1619 if (cm.isReadOnly() || !input.pollContent())
1620 runInOp(input.cm, function() {regChange(cm);});
1621 });
1622
1623 function onCopyCut(e) {
1624 if (signalDOMEvent(cm, e)) return
1625 if (cm.somethingSelected()) {
1626 lastCopied = {lineWise: false, text: cm.getSelections()};
1627 if (e.type == "cut") cm.replaceSelection("", null, "cut");
1628 } else if (!cm.options.lineWiseCopyCut) {
1629 return;
1630 } else {
1631 var ranges = copyableRanges(cm);
1632 lastCopied = {lineWise: true, text: ranges.text};
1633 if (e.type == "cut") {
1634 cm.operation(function() {
1635 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
1636 cm.replaceSelection("", null, "cut");
1637 });
1638 }
1639 }
1640 // iOS exposes the clipboard API, but seems to discard content inserted into it
1641 if (e.clipboardData && !ios) {
1642 e.preventDefault();
1643 e.clipboardData.clearData();
1644 e.clipboardData.setData("text/plain", lastCopied.text.join("\n"));
1645 } else {
1646 // Old-fashioned briefly-focus-a-textarea hack
1647 var kludge = hiddenTextarea(), te = kludge.firstChild;
1648 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstCh ild);
1649 te.value = lastCopied.text.join("\n");
1650 var hadFocus = document.activeElement;
1651 selectInput(te);
1652 setTimeout(function() {
1653 cm.display.lineSpace.removeChild(kludge);
1654 hadFocus.focus();
1655 }, 50);
1656 }
1657 }
1658 on(div, "copy", onCopyCut);
1659 on(div, "cut", onCopyCut);
1660 },
1661
1662 prepareSelection: function() {
1663 var result = prepareSelection(this.cm, false);
1664 result.focus = this.cm.state.focused;
1665 return result;
1666 },
1667
1668 showSelection: function(info, takeFocus) {
1669 if (!info || !this.cm.display.view.length) return;
1670 if (info.focus || takeFocus) this.showPrimarySelection();
1671 this.showMultipleSelections(info);
1672 },
1673
1674 showPrimarySelection: function() {
1675 var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
1676 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
1677 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
1678 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
1679 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
1680 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
1681 return;
1682
1683 var start = posToDOM(this.cm, prim.from());
1684 var end = posToDOM(this.cm, prim.to());
1685 if (!start && !end) return;
1686
1687 var view = this.cm.display.view;
1688 var old = sel.rangeCount && sel.getRangeAt(0);
1689 if (!start) {
1690 start = {node: view[0].measure.map[2], offset: 0};
1691 } else if (!end) { // FIXME dangerously hacky
1692 var measure = view[view.length - 1].measure;
1693 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure .map;
1694 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map. length - 3]};
1695 }
1696
1697 try { var rng = range(start.node, start.offset, end.offset, end.node); }
1698 catch(e) {} // Our model of the DOM might be outdated, in which case the r ange we try to set can be impossible
1699 if (rng) {
1700 if (!gecko && this.cm.state.focused) {
1701 sel.collapse(start.node, start.offset);
1702 if (!rng.collapsed) sel.addRange(rng);
1703 } else {
1704 sel.removeAllRanges();
1705 sel.addRange(rng);
1706 }
1707 if (old && sel.anchorNode == null) sel.addRange(old);
1708 else if (gecko) this.startGracePeriod();
1709 }
1710 this.rememberSelection();
1711 },
1712
1713 startGracePeriod: function() {
1714 var input = this;
1715 clearTimeout(this.gracePeriod);
1716 this.gracePeriod = setTimeout(function() {
1717 input.gracePeriod = false;
1718 if (input.selectionChanged())
1719 input.cm.operation(function() { input.cm.curOp.selectionChanged = true ; });
1720 }, 20);
1721 },
1722
1723 showMultipleSelections: function(info) {
1724 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
1725 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
1726 },
1727
1728 rememberSelection: function() {
1729 var sel = window.getSelection();
1730 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOf fset;
1731 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset ;
1732 },
1733
1734 selectionInEditor: function() {
1735 var sel = window.getSelection();
1736 if (!sel.rangeCount) return false;
1737 var node = sel.getRangeAt(0).commonAncestorContainer;
1738 return contains(this.div, node);
1739 },
1740
1741 focus: function() {
1742 if (this.cm.options.readOnly != "nocursor") this.div.focus();
1743 },
1744 blur: function() { this.div.blur(); },
1745 getField: function() { return this.div; },
1746
1747 supportsTouch: function() { return true; },
1748
1749 receivedFocus: function() {
1750 var input = this;
1751 if (this.selectionInEditor())
1752 this.pollSelection();
1753 else
1754 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; }) ;
1755
1756 function poll() {
1757 if (input.cm.state.focused) {
1758 input.pollSelection();
1759 input.polling.set(input.cm.options.pollInterval, poll);
1760 }
1761 }
1762 this.polling.set(this.cm.options.pollInterval, poll);
1763 },
1764
1765 selectionChanged: function() {
1766 var sel = window.getSelection();
1767 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.l astAnchorOffset ||
1768 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocus Offset;
1769 },
1770
1771 pollSelection: function() {
1772 if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
1773 var sel = window.getSelection(), cm = this.cm;
1774 this.rememberSelection();
1775 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
1776 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
1777 if (anchor && head) runInOp(cm, function() {
1778 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
1779 if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
1780 });
1781 }
1782 },
1783
1784 pollContent: function() {
1785 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
1786 var from = sel.from(), to = sel.to();
1787 if (from.line < display.viewFrom || to.line > display.viewTo - 1) return f alse;
1788
1789 var fromIndex;
1790 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.l ine)) == 0) {
1791 var fromLine = lineNo(display.view[0].line);
1792 var fromNode = display.view[0].node;
1793 } else {
1794 var fromLine = lineNo(display.view[fromIndex].line);
1795 var fromNode = display.view[fromIndex - 1].node.nextSibling;
1796 }
1797 var toIndex = findViewIndex(cm, to.line);
1798 if (toIndex == display.view.length - 1) {
1799 var toLine = display.viewTo - 1;
1800 var toNode = display.lineDiv.lastChild;
1801 } else {
1802 var toLine = lineNo(display.view[toIndex + 1].line) - 1;
1803 var toNode = display.view[toIndex + 1].node.previousSibling;
1804 }
1805
1806 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromL ine, toLine));
1807 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm. doc, toLine).text.length));
1808 while (newText.length > 1 && oldText.length > 1) {
1809 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine --; }
1810 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); f romLine++; }
1811 else break;
1812 }
1813
1814 var cutFront = 0, cutEnd = 0;
1815 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTo p.length, oldTop.length);
1816 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.cha rCodeAt(cutFront))
1817 ++cutFront;
1818 var newBot = lst(newText), oldBot = lst(oldText);
1819 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
1820 oldBot.length - (oldText.length == 1 ? cutFront : 0));
1821 while (cutEnd < maxCutEnd &&
1822 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt( oldBot.length - cutEnd - 1))
1823 ++cutEnd;
1824
1825 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
1826 newText[0] = newText[0].slice(cutFront);
1827
1828 var chFrom = Pos(fromLine, cutFront);
1829 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
1830 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
1831 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
1832 return true;
1833 }
1834 },
1835
1836 ensurePolled: function() {
1837 this.forceCompositionEnd();
1838 },
1839 reset: function() {
1840 this.forceCompositionEnd();
1841 },
1842 forceCompositionEnd: function() {
1843 if (!this.composing || this.composing.handled) return;
1844 this.applyComposition(this.composing);
1845 this.composing.handled = true;
1846 this.div.blur();
1847 this.div.focus();
1848 },
1849 applyComposition: function(composing) {
1850 if (this.cm.isReadOnly())
1851 operation(this.cm, regChange)(this.cm)
1852 else if (composing.data && composing.data != composing.startData)
1853 operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing .sel);
1854 },
1855
1856 setUneditable: function(node) {
1857 node.contentEditable = "false"
1858 },
1859
1860 onKeyPress: function(e) {
1861 e.preventDefault();
1862 if (!this.cm.isReadOnly())
1863 operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCo de == null ? e.keyCode : e.charCode), 0);
1864 },
1865
1866 readOnlyChanged: function(val) {
1867 this.div.contentEditable = String(val != "nocursor")
1868 },
1869
1870 onContextMenu: nothing,
1871 resetPosition: nothing,
1872
1873 needsContentAttribute: true
1874 }, ContentEditableInput.prototype);
1875
1876 function posToDOM(cm, pos) {
1877 var view = findViewForLine(cm, pos.line);
1878 if (!view || view.hidden) return null;
1879 var line = getLine(cm.doc, pos.line);
1880 var info = mapFromLineView(view, line, pos.line);
1881
1882 var order = getOrder(line), side = "left";
1883 if (order) {
1884 var partPos = getBidiPartAt(order, pos.ch);
1885 side = partPos % 2 ? "right" : "left";
1886 }
1887 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
1888 result.offset = result.collapse == "right" ? result.end : result.start;
1889 return result;
1890 }
1891
1892 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
1893
1894 function domToPos(cm, node, offset) {
1895 var lineNode;
1896 if (node == cm.display.lineDiv) {
1897 lineNode = cm.display.lineDiv.childNodes[offset];
1898 if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) ;
1899 node = null; offset = 0;
1900 } else {
1901 for (lineNode = node;; lineNode = lineNode.parentNode) {
1902 if (!lineNode || lineNode == cm.display.lineDiv) return null;
1903 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) br eak;
1904 }
1905 }
1906 for (var i = 0; i < cm.display.view.length; i++) {
1907 var lineView = cm.display.view[i];
1908 if (lineView.node == lineNode)
1909 return locateNodeInLineView(lineView, node, offset);
1910 }
1911 }
1912
1913 function locateNodeInLineView(lineView, node, offset) {
1914 var wrapper = lineView.text.firstChild, bad = false;
1915 if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.lin e), 0), true);
1916 if (node == wrapper) {
1917 bad = true;
1918 node = wrapper.childNodes[offset];
1919 offset = 0;
1920 if (!node) {
1921 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
1922 return badPos(Pos(lineNo(line), line.text.length), bad);
1923 }
1924 }
1925
1926 var textNode = node.nodeType == 3 ? node : null, topNode = node;
1927 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
1928 textNode = node.firstChild;
1929 if (offset) offset = textNode.nodeValue.length;
1930 }
1931 while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
1932 var measure = lineView.measure, maps = measure.maps;
1933
1934 function find(textNode, topNode, offset) {
1935 for (var i = -1; i < (maps ? maps.length : 0); i++) {
1936 var map = i < 0 ? measure.map : maps[i];
1937 for (var j = 0; j < map.length; j += 3) {
1938 var curNode = map[j + 2];
1939 if (curNode == textNode || curNode == topNode) {
1940 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
1941 var ch = map[j] + offset;
1942 if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0) ];
1943 return Pos(line, ch);
1944 }
1945 }
1946 }
1947 }
1948 var found = find(textNode, topNode, offset);
1949 if (found) return badPos(found, bad);
1950
1951 // FIXME this is all really shaky. might handle the few cases it needs to ha ndle, but likely to cause problems
1952 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.l ength - offset : 0; after; after = after.nextSibling) {
1953 found = find(after, after.firstChild, 0);
1954 if (found)
1955 return badPos(Pos(found.line, found.ch - dist), bad);
1956 else
1957 dist += after.textContent.length;
1958 }
1959 for (var before = topNode.previousSibling, dist = offset; before; before = b efore.previousSibling) {
1960 found = find(before, before.firstChild, -1);
1961 if (found)
1962 return badPos(Pos(found.line, found.ch + dist), bad);
1963 else
1964 dist += after.textContent.length;
1965 }
1966 }
1967
1968 function domTextBetween(cm, from, to, fromLine, toLine) {
1969 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
1970 function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
1971 function walk(node) {
1972 if (node.nodeType == 1) {
1973 var cmText = node.getAttribute("cm-text");
1974 if (cmText != null) {
1975 if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
1976 text += cmText;
1977 return;
1978 }
1979 var markerID = node.getAttribute("cm-marker"), range;
1980 if (markerID) {
1981 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recogni zeMarker(+markerID));
1982 if (found.length && (range = found[0].find()))
1983 text += getBetween(cm.doc, range.from, range.to).join(lineSep);
1984 return;
1985 }
1986 if (node.getAttribute("contenteditable") == "false") return;
1987 for (var i = 0; i < node.childNodes.length; i++)
1988 walk(node.childNodes[i]);
1989 if (/^(pre|div|p)$/i.test(node.nodeName))
1990 closing = true;
1991 } else if (node.nodeType == 3) {
1992 var val = node.nodeValue;
1993 if (!val) return;
1994 if (closing) {
1995 text += lineSep;
1996 closing = false;
1997 }
1998 text += val;
1999 }
2000 }
2001 for (;;) {
2002 walk(from);
2003 if (from == to) break;
2004 from = from.nextSibling;
2005 }
2006 return text;
2007 }
2008
2009 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": Conten tEditableInput};
2010
1075 // SELECTION / CURSOR 2011 // SELECTION / CURSOR
1076 2012
1077 // Selection objects are immutable. A new one is created every time 2013 // Selection objects are immutable. A new one is created every time
1078 // the selection changes. A selection is one or more non-overlapping 2014 // the selection changes. A selection is one or more non-overlapping
1079 // (and non-touching) ranges, sorted, and an integer that indicates 2015 // (and non-touching) ranges, sorted, and an integer that indicates
1080 // which one is the primary selection (the one that's scrolled into 2016 // which one is the primary selection (the one that's scrolled into
1081 // view, that getCursor returns, etc). 2017 // view, that getCursor returns, etc).
1082 function Selection(ranges, primIndex) { 2018 function Selection(ranges, primIndex) {
1083 this.ranges = ranges; 2019 this.ranges = ranges;
1084 this.primIndex = primIndex; 2020 this.primIndex = primIndex;
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
1221 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); 2157 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
1222 } 2158 }
1223 2159
1224 // Reset the selection to a single range. 2160 // Reset the selection to a single range.
1225 function setSimpleSelection(doc, anchor, head, options) { 2161 function setSimpleSelection(doc, anchor, head, options) {
1226 setSelection(doc, simpleSelection(anchor, head), options); 2162 setSelection(doc, simpleSelection(anchor, head), options);
1227 } 2163 }
1228 2164
1229 // Give beforeSelectionChange handlers a change to influence a 2165 // Give beforeSelectionChange handlers a change to influence a
1230 // selection update. 2166 // selection update.
1231 function filterSelectionChange(doc, sel) { 2167 function filterSelectionChange(doc, sel, options) {
1232 var obj = { 2168 var obj = {
1233 ranges: sel.ranges, 2169 ranges: sel.ranges,
1234 update: function(ranges) { 2170 update: function(ranges) {
1235 this.ranges = []; 2171 this.ranges = [];
1236 for (var i = 0; i < ranges.length; i++) 2172 for (var i = 0; i < ranges.length; i++)
1237 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), 2173 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
1238 clipPos(doc, ranges[i].head)); 2174 clipPos(doc, ranges[i].head));
1239 } 2175 },
2176 origin: options && options.origin
1240 }; 2177 };
1241 signal(doc, "beforeSelectionChange", doc, obj); 2178 signal(doc, "beforeSelectionChange", doc, obj);
1242 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); 2179 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
1243 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.rang es.length - 1); 2180 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.rang es.length - 1);
1244 else return sel; 2181 else return sel;
1245 } 2182 }
1246 2183
1247 function setSelectionReplaceHistory(doc, sel, options) { 2184 function setSelectionReplaceHistory(doc, sel, options) {
1248 var done = doc.history.done, last = lst(done); 2185 var done = doc.history.done, last = lst(done);
1249 if (last && last.ranges) { 2186 if (last && last.ranges) {
1250 done[done.length - 1] = sel; 2187 done[done.length - 1] = sel;
1251 setSelectionNoUndo(doc, sel, options); 2188 setSelectionNoUndo(doc, sel, options);
1252 } else { 2189 } else {
1253 setSelection(doc, sel, options); 2190 setSelection(doc, sel, options);
1254 } 2191 }
1255 } 2192 }
1256 2193
1257 // Set a new selection. 2194 // Set a new selection.
1258 function setSelection(doc, sel, options) { 2195 function setSelection(doc, sel, options) {
1259 setSelectionNoUndo(doc, sel, options); 2196 setSelectionNoUndo(doc, sel, options);
1260 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options) ; 2197 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options) ;
1261 } 2198 }
1262 2199
1263 function setSelectionNoUndo(doc, sel, options) { 2200 function setSelectionNoUndo(doc, sel, options) {
1264 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) 2201 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
1265 sel = filterSelectionChange(doc, sel); 2202 sel = filterSelectionChange(doc, sel, options);
1266 2203
1267 var bias = options && options.bias || 2204 var bias = options && options.bias ||
1268 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); 2205 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
1269 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); 2206 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
1270 2207
1271 if (!(options && options.scroll === false) && doc.cm) 2208 if (!(options && options.scroll === false) && doc.cm)
1272 ensureCursorVisible(doc.cm); 2209 ensureCursorVisible(doc.cm);
1273 } 2210 }
1274 2211
1275 function setSelectionInner(doc, sel) { 2212 function setSelectionInner(doc, sel) {
(...skipping 13 matching lines...) Expand all
1289 function reCheckSelection(doc) { 2226 function reCheckSelection(doc) {
1290 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel _dontScroll); 2227 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel _dontScroll);
1291 } 2228 }
1292 2229
1293 // Return a selection that does not partially select any atomic 2230 // Return a selection that does not partially select any atomic
1294 // ranges. 2231 // ranges.
1295 function skipAtomicInSelection(doc, sel, bias, mayClear) { 2232 function skipAtomicInSelection(doc, sel, bias, mayClear) {
1296 var out; 2233 var out;
1297 for (var i = 0; i < sel.ranges.length; i++) { 2234 for (var i = 0; i < sel.ranges.length; i++) {
1298 var range = sel.ranges[i]; 2235 var range = sel.ranges[i];
1299 var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear); 2236 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
1300 var newHead = skipAtomic(doc, range.head, bias, mayClear); 2237 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, may Clear);
2238 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear) ;
1301 if (out || newAnchor != range.anchor || newHead != range.head) { 2239 if (out || newAnchor != range.anchor || newHead != range.head) {
1302 if (!out) out = sel.ranges.slice(0, i); 2240 if (!out) out = sel.ranges.slice(0, i);
1303 out[i] = new Range(newAnchor, newHead); 2241 out[i] = new Range(newAnchor, newHead);
1304 } 2242 }
1305 } 2243 }
1306 return out ? normalizeSelection(out, sel.primIndex) : sel; 2244 return out ? normalizeSelection(out, sel.primIndex) : sel;
1307 } 2245 }
1308 2246
1309 // Ensure a given position is not inside an atomic range. 2247 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
1310 function skipAtomic(doc, pos, bias, mayClear) { 2248 var line = getLine(doc, pos.line);
1311 var flipped = false, curPos = pos; 2249 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
1312 var dir = bias || 1; 2250 var sp = line.markedSpans[i], m = sp.marker;
1313 doc.cantEdit = false; 2251 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < p os.ch)) &&
1314 search: for (;;) { 2252 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch ))) {
1315 var line = getLine(doc, curPos.line); 2253 if (mayClear) {
1316 if (line.markedSpans) { 2254 signal(m, "beforeCursorEnter");
1317 for (var i = 0; i < line.markedSpans.length; ++i) { 2255 if (m.explicitlyCleared) {
1318 var sp = line.markedSpans[i], m = sp.marker; 2256 if (!line.markedSpans) break;
1319 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.f rom < curPos.ch)) && 2257 else {--i; continue;}
1320 (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
1321 if (mayClear) {
1322 signal(m, "beforeCursorEnter");
1323 if (m.explicitlyCleared) {
1324 if (!line.markedSpans) break;
1325 else {--i; continue;}
1326 }
1327 }
1328 if (!m.atomic) continue;
1329 var newPos = m.find(dir < 0 ? -1 : 1);
1330 if (cmp(newPos, curPos) == 0) {
1331 newPos.ch += dir;
1332 if (newPos.ch < 0) {
1333 if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.li ne - 1));
1334 else newPos = null;
1335 } else if (newPos.ch > line.text.length) {
1336 if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos. line + 1, 0);
1337 else newPos = null;
1338 }
1339 if (!newPos) {
1340 if (flipped) {
1341 // Driven in a corner -- no valid cursor position found at all
1342 // -- try again *with* clearing, if we didn't already
1343 if (!mayClear) return skipAtomic(doc, pos, bias, true);
1344 // Otherwise, turn off editing until further notice, and retur n the start of the doc
1345 doc.cantEdit = true;
1346 return Pos(doc.first, 0);
1347 }
1348 flipped = true; newPos = pos; dir = -dir;
1349 }
1350 }
1351 curPos = newPos;
1352 continue search;
1353 } 2258 }
1354 } 2259 }
2260 if (!m.atomic) continue;
2261
2262 if (oldPos) {
2263 var near = m.find(dir < 0 ? 1 : -1), diff;
2264 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
2265 near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null);
2266 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (di r < 0 ? diff < 0 : diff > 0))
2267 return skipAtomicInner(doc, near, pos, dir, mayClear);
2268 }
2269
2270 var far = m.find(dir < 0 ? -1 : 1);
2271 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
2272 far = movePos(doc, far, dir, far.line == pos.line ? line : null);
2273 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null;
1355 } 2274 }
1356 return curPos; 2275 }
2276 return pos;
2277 }
2278
2279 // Ensure a given position is not inside an atomic range.
2280 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
2281 var dir = bias || 1;
2282 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
2283 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
2284 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
2285 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
2286 if (!found) {
2287 doc.cantEdit = true;
2288 return Pos(doc.first, 0);
2289 }
2290 return found;
2291 }
2292
2293 function movePos(doc, pos, dir, line) {
2294 if (dir < 0 && pos.ch == 0) {
2295 if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1));
2296 else return null;
2297 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length ) {
2298 if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0);
2299 else return null;
2300 } else {
2301 return new Pos(pos.line, pos.ch + dir);
1357 } 2302 }
1358 } 2303 }
1359 2304
1360 // SELECTION DRAWING 2305 // SELECTION DRAWING
1361 2306
1362 // Redraw the selection and/or cursor 2307 function updateSelection(cm) {
1363 function drawSelection(cm) { 2308 cm.display.input.showSelection(cm.display.input.prepareSelection());
1364 var display = cm.display, doc = cm.doc, result = {}; 2309 }
2310
2311 function prepareSelection(cm, primary) {
2312 var doc = cm.doc, result = {};
1365 var curFragment = result.cursors = document.createDocumentFragment(); 2313 var curFragment = result.cursors = document.createDocumentFragment();
1366 var selFragment = result.selection = document.createDocumentFragment(); 2314 var selFragment = result.selection = document.createDocumentFragment();
1367 2315
1368 for (var i = 0; i < doc.sel.ranges.length; i++) { 2316 for (var i = 0; i < doc.sel.ranges.length; i++) {
2317 if (primary === false && i == doc.sel.primIndex) continue;
1369 var range = doc.sel.ranges[i]; 2318 var range = doc.sel.ranges[i];
2319 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display .viewFrom) continue;
1370 var collapsed = range.empty(); 2320 var collapsed = range.empty();
1371 if (collapsed || cm.options.showCursorWhenSelecting) 2321 if (collapsed || cm.options.showCursorWhenSelecting)
1372 drawSelectionCursor(cm, range, curFragment); 2322 drawSelectionCursor(cm, range.head, curFragment);
1373 if (!collapsed) 2323 if (!collapsed)
1374 drawSelectionRange(cm, range, selFragment); 2324 drawSelectionRange(cm, range, selFragment);
1375 } 2325 }
1376
1377 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1378 if (cm.options.moveInputWithCursor) {
1379 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1380 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.l ineDiv.getBoundingClientRect();
1381 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1382 headPos.top + lineOff.top - wrapOff.to p));
1383 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1384 headPos.left + lineOff.left - wrapOff .left));
1385 }
1386
1387 return result; 2326 return result;
1388 } 2327 }
1389 2328
1390 function showSelection(cm, drawn) {
1391 removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors);
1392 removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection);
1393 if (drawn.teTop != null) {
1394 cm.display.inputDiv.style.top = drawn.teTop + "px";
1395 cm.display.inputDiv.style.left = drawn.teLeft + "px";
1396 }
1397 }
1398
1399 function updateSelection(cm) {
1400 showSelection(cm, drawSelection(cm));
1401 }
1402
1403 // Draws a cursor for the given range 2329 // Draws a cursor for the given range
1404 function drawSelectionCursor(cm, range, output) { 2330 function drawSelectionCursor(cm, head, output) {
1405 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.single CursorHeightPerLine); 2331 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursor HeightPerLine);
1406 2332
1407 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); 2333 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
1408 cursor.style.left = pos.left + "px"; 2334 cursor.style.left = pos.left + "px";
1409 cursor.style.top = pos.top + "px"; 2335 cursor.style.top = pos.top + "px";
1410 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorH eight + "px"; 2336 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorH eight + "px";
1411 2337
1412 if (pos.other) { 2338 if (pos.other) {
1413 // Secondary cursor, shown when on a 'jump' in bi-directional text 2339 // Secondary cursor, shown when on a 'jump' in bi-directional text
1414 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-curs or CodeMirror-secondarycursor")); 2340 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-curs or CodeMirror-secondarycursor"));
1415 otherCursor.style.display = ""; 2341 otherCursor.style.display = "";
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
1519 function highlightWorker(cm) { 2445 function highlightWorker(cm) {
1520 var doc = cm.doc; 2446 var doc = cm.doc;
1521 if (doc.frontier < doc.first) doc.frontier = doc.first; 2447 if (doc.frontier < doc.first) doc.frontier = doc.first;
1522 if (doc.frontier >= cm.display.viewTo) return; 2448 if (doc.frontier >= cm.display.viewTo) return;
1523 var end = +new Date + cm.options.workTime; 2449 var end = +new Date + cm.options.workTime;
1524 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); 2450 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
1525 var changedLines = []; 2451 var changedLines = [];
1526 2452
1527 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 50 0), function(line) { 2453 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 50 0), function(line) {
1528 if (doc.frontier >= cm.display.viewFrom) { // Visible 2454 if (doc.frontier >= cm.display.viewFrom) { // Visible
1529 var oldStyles = line.styles; 2455 var oldStyles = line.styles, tooLong = line.text.length > cm.options.max HighlightLength;
1530 var highlighted = highlightLine(cm, line, state, true); 2456 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
1531 line.styles = highlighted.styles; 2457 line.styles = highlighted.styles;
1532 var oldCls = line.styleClasses, newCls = highlighted.classes; 2458 var oldCls = line.styleClasses, newCls = highlighted.classes;
1533 if (newCls) line.styleClasses = newCls; 2459 if (newCls) line.styleClasses = newCls;
1534 else if (oldCls) line.styleClasses = null; 2460 else if (oldCls) line.styleClasses = null;
1535 var ischange = !oldStyles || oldStyles.length != line.styles.length || 2461 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
1536 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bg Class || oldCls.textClass != newCls.textClass); 2462 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bg Class || oldCls.textClass != newCls.textClass);
1537 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldSt yles[i] != line.styles[i]; 2463 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldSt yles[i] != line.styles[i];
1538 if (ischange) changedLines.push(doc.frontier); 2464 if (ischange) changedLines.push(doc.frontier);
1539 line.stateAfter = copyState(doc.mode, state); 2465 line.stateAfter = tooLong ? state : copyState(doc.mode, state);
1540 } else { 2466 } else {
1541 processLine(cm, line.text, state); 2467 if (line.text.length <= cm.options.maxHighlightLength)
2468 processLine(cm, line.text, state);
1542 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : n ull; 2469 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : n ull;
1543 } 2470 }
1544 ++doc.frontier; 2471 ++doc.frontier;
1545 if (+new Date > end) { 2472 if (+new Date > end) {
1546 startWorker(cm, cm.options.workDelay); 2473 startWorker(cm, cm.options.workDelay);
1547 return true; 2474 return true;
1548 } 2475 }
1549 }); 2476 });
1550 if (changedLines.length) runInOp(cm, function() { 2477 if (changedLines.length) runInOp(cm, function() {
1551 for (var i = 0; i < changedLines.length; i++) 2478 for (var i = 0; i < changedLines.length; i++)
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
1676 } 2603 }
1677 2604
1678 // Measurement can be split in two steps, the set-up work that 2605 // Measurement can be split in two steps, the set-up work that
1679 // applies to the whole line, and the measurement of the actual 2606 // applies to the whole line, and the measurement of the actual
1680 // character. Functions like coordsChar, that need to do a lot of 2607 // character. Functions like coordsChar, that need to do a lot of
1681 // measurements in a row, can thus ensure that the set-up work is 2608 // measurements in a row, can thus ensure that the set-up work is
1682 // only done once. 2609 // only done once.
1683 function prepareMeasureForLine(cm, line) { 2610 function prepareMeasureForLine(cm, line) {
1684 var lineN = lineNo(line); 2611 var lineN = lineNo(line);
1685 var view = findViewForLine(cm, lineN); 2612 var view = findViewForLine(cm, lineN);
1686 if (view && !view.text) 2613 if (view && !view.text) {
1687 view = null; 2614 view = null;
1688 else if (view && view.changes) 2615 } else if (view && view.changes) {
1689 updateLineForChanges(cm, view, lineN, getDimensions(cm)); 2616 updateLineForChanges(cm, view, lineN, getDimensions(cm));
2617 cm.curOp.forceUpdate = true;
2618 }
1690 if (!view) 2619 if (!view)
1691 view = updateExternalMeasurement(cm, line); 2620 view = updateExternalMeasurement(cm, line);
1692 2621
1693 var info = mapFromLineView(view, line, lineN); 2622 var info = mapFromLineView(view, line, lineN);
1694 return { 2623 return {
1695 line: line, view: view, rect: null, 2624 line: line, view: view, rect: null,
1696 map: info.map, cache: info.cache, before: info.before, 2625 map: info.map, cache: info.cache, before: info.before,
1697 hasHeights: false 2626 hasHeights: false
1698 }; 2627 };
1699 } 2628 }
(...skipping 15 matching lines...) Expand all
1715 found = measureCharInner(cm, prepared, ch, bias); 2644 found = measureCharInner(cm, prepared, ch, bias);
1716 if (!found.bogus) prepared.cache[key] = found; 2645 if (!found.bogus) prepared.cache[key] = found;
1717 } 2646 }
1718 return {left: found.left, right: found.right, 2647 return {left: found.left, right: found.right,
1719 top: varHeight ? found.rtop : found.top, 2648 top: varHeight ? found.rtop : found.top,
1720 bottom: varHeight ? found.rbottom : found.bottom}; 2649 bottom: varHeight ? found.rbottom : found.bottom};
1721 } 2650 }
1722 2651
1723 var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; 2652 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
1724 2653
1725 function measureCharInner(cm, prepared, ch, bias) { 2654 function nodeAndOffsetInLineMap(map, ch, bias) {
1726 var map = prepared.map;
1727
1728 var node, start, end, collapse; 2655 var node, start, end, collapse;
1729 // First, search the line map for the text node corresponding to, 2656 // First, search the line map for the text node corresponding to,
1730 // or closest to, the target character. 2657 // or closest to, the target character.
1731 for (var i = 0; i < map.length; i += 3) { 2658 for (var i = 0; i < map.length; i += 3) {
1732 var mStart = map[i], mEnd = map[i + 1]; 2659 var mStart = map[i], mEnd = map[i + 1];
1733 if (ch < mStart) { 2660 if (ch < mStart) {
1734 start = 0; end = 1; 2661 start = 0; end = 1;
1735 collapse = "left"; 2662 collapse = "left";
1736 } else if (ch < mEnd) { 2663 } else if (ch < mEnd) {
1737 start = ch - mStart; 2664 start = ch - mStart;
(...skipping 13 matching lines...) Expand all
1751 collapse = "left"; 2678 collapse = "left";
1752 } 2679 }
1753 if (bias == "right" && start == mEnd - mStart) 2680 if (bias == "right" && start == mEnd - mStart)
1754 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].i nsertLeft) { 2681 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].i nsertLeft) {
1755 node = map[(i += 3) + 2]; 2682 node = map[(i += 3) + 2];
1756 collapse = "right"; 2683 collapse = "right";
1757 } 2684 }
1758 break; 2685 break;
1759 } 2686 }
1760 } 2687 }
2688 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
2689 }
2690
2691 function getUsefulRect(rects, bias) {
2692 var rect = nullRect
2693 if (bias == "left") for (var i = 0; i < rects.length; i++) {
2694 if ((rect = rects[i]).left != rect.right) break
2695 } else for (var i = rects.length - 1; i >= 0; i--) {
2696 if ((rect = rects[i]).left != rect.right) break
2697 }
2698 return rect
2699 }
2700
2701 function measureCharInner(cm, prepared, ch, bias) {
2702 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
2703 var node = place.node, start = place.start, end = place.end, collapse = plac e.collapse;
1761 2704
1762 var rect; 2705 var rect;
1763 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve t he coordinates. 2706 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve t he coordinates.
1764 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned 2707 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
1765 while (start && isExtendingChar(prepared.line.text.charAt(mStart + start ))) --start; 2708 while (start && isExtendingChar(prepared.line.text.charAt(place.coverSta rt + start))) --start;
1766 while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt( mStart + end))) ++end; 2709 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepar ed.line.text.charAt(place.coverStart + end))) ++end;
1767 if (ie && ie_version < 9 && start == 0 && end == mEnd - mStart) { 2710 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place. coverStart)
1768 rect = node.parentNode.getBoundingClientRect(); 2711 rect = node.parentNode.getBoundingClientRect();
1769 } else if (ie && cm.options.lineWrapping) { 2712 else
1770 var rects = range(node, start, end).getClientRects(); 2713 rect = getUsefulRect(range(node, start, end).getClientRects(), bias)
1771 if (rects.length)
1772 rect = rects[bias == "right" ? rects.length - 1 : 0];
1773 else
1774 rect = nullRect;
1775 } else {
1776 rect = range(node, start, end).getBoundingClientRect() || nullRect;
1777 }
1778 if (rect.left || rect.right || start == 0) break; 2714 if (rect.left || rect.right || start == 0) break;
1779 end = start; 2715 end = start;
1780 start = start - 1; 2716 start = start - 1;
1781 collapse = "right"; 2717 collapse = "right";
1782 } 2718 }
1783 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.mea sure, rect); 2719 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.mea sure, rect);
1784 } else { // If it is a widget, simply get the box for the whole widget. 2720 } else { // If it is a widget, simply get the box for the whole widget.
1785 if (start > 0) collapse = bias = "right"; 2721 if (start > 0) collapse = bias = "right";
1786 var rects; 2722 var rects;
1787 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) 2723 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
1993 2929
1994 var bidi = getOrder(lineObj), dist = lineObj.text.length; 2930 var bidi = getOrder(lineObj), dist = lineObj.text.length;
1995 var from = lineLeft(lineObj), to = lineRight(lineObj); 2931 var from = lineLeft(lineObj), to = lineRight(lineObj);
1996 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine; 2932 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
1997 2933
1998 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1); 2934 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
1999 // Do a binary search between these bounds. 2935 // Do a binary search between these bounds.
2000 for (;;) { 2936 for (;;) {
2001 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { 2937 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
2002 var ch = x < fromX || x - fromX <= toX - x ? from : to; 2938 var ch = x < fromX || x - fromX <= toX - x ? from : to;
2939 var outside = ch == from ? fromOutside : toOutside
2003 var xDiff = x - (ch == from ? fromX : toX); 2940 var xDiff = x - (ch == from ? fromX : toX);
2941 // This is a kludge to handle the case where the coordinates
2942 // are after a line-wrapped line. We should replace it with a
2943 // more general handling of cursor positions around line
2944 // breaks. (Issue #4078)
2945 if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 &&
2946 ch < lineObj.text.length && preparedMeasure.view.measure.heights.len gth > 1) {
2947 var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right");
2948 if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.ab s(x - charSize.right) < xDiff) {
2949 outside = false
2950 ch++
2951 xDiff = x - charSize.right
2952 }
2953 }
2004 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch; 2954 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
2005 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside, 2955 var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2006 xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
2007 return pos; 2956 return pos;
2008 } 2957 }
2009 var step = Math.ceil(dist / 2), middle = from + step; 2958 var step = Math.ceil(dist / 2), middle = from + step;
2010 if (bidi) { 2959 if (bidi) {
2011 middle = from; 2960 middle = from;
2012 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1) ; 2961 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1) ;
2013 } 2962 }
2014 var middleX = getX(middle); 2963 var middleX = getX(middle);
2015 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) t oX += 1000; dist = step;} 2964 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) t oX += 1000; dist = step;}
2016 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= ste p;} 2965 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= ste p;}
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
2069 forceUpdate: false, // Used to force a redraw 3018 forceUpdate: false, // Used to force a redraw
2070 updateInput: null, // Whether to reset the input textarea 3019 updateInput: null, // Whether to reset the input textarea
2071 typing: false, // Whether this reset should be careful to leave existing text (for compositing) 3020 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
2072 changeObjs: null, // Accumulated changes, for firing change events 3021 changeObjs: null, // Accumulated changes, for firing change events
2073 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on 3022 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
2074 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already 3023 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
2075 selectionChanged: false, // Whether the selection needs to be redrawn 3024 selectionChanged: false, // Whether the selection needs to be redrawn
2076 updateMaxLine: false, // Set when the widest line needs to be determine d anew 3025 updateMaxLine: false, // Set when the widest line needs to be determine d anew
2077 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pu shed to DOM yet 3026 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pu shed to DOM yet
2078 scrollToPos: null, // Used to scroll to a specific position 3027 scrollToPos: null, // Used to scroll to a specific position
3028 focus: false,
2079 id: ++nextOpId // Unique ID 3029 id: ++nextOpId // Unique ID
2080 }; 3030 };
2081 if (operationGroup) { 3031 if (operationGroup) {
2082 operationGroup.ops.push(cm.curOp); 3032 operationGroup.ops.push(cm.curOp);
2083 } else { 3033 } else {
2084 cm.curOp.ownsGroup = operationGroup = { 3034 cm.curOp.ownsGroup = operationGroup = {
2085 ops: [cm.curOp], 3035 ops: [cm.curOp],
2086 delayedCallbacks: [] 3036 delayedCallbacks: []
2087 }; 3037 };
2088 } 3038 }
2089 } 3039 }
2090 3040
2091 function fireCallbacksForOps(group) { 3041 function fireCallbacksForOps(group) {
2092 // Calls delayed callbacks and cursorActivity handlers until no 3042 // Calls delayed callbacks and cursorActivity handlers until no
2093 // new ones appear 3043 // new ones appear
2094 var callbacks = group.delayedCallbacks, i = 0; 3044 var callbacks = group.delayedCallbacks, i = 0;
2095 do { 3045 do {
2096 for (; i < callbacks.length; i++) 3046 for (; i < callbacks.length; i++)
2097 callbacks[i](); 3047 callbacks[i].call(null);
2098 for (var j = 0; j < group.ops.length; j++) { 3048 for (var j = 0; j < group.ops.length; j++) {
2099 var op = group.ops[j]; 3049 var op = group.ops[j];
2100 if (op.cursorActivityHandlers) 3050 if (op.cursorActivityHandlers)
2101 while (op.cursorActivityCalled < op.cursorActivityHandlers.length) 3051 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
2102 op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm); 3052 op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.c m);
2103 } 3053 }
2104 } while (i < callbacks.length); 3054 } while (i < callbacks.length);
2105 } 3055 }
2106 3056
2107 // Finish an operation, updating the display and signalling delayed events 3057 // Finish an operation, updating the display and signalling delayed events
2108 function endOperation(cm) { 3058 function endOperation(cm) {
2109 var op = cm.curOp, group = op.ownsGroup; 3059 var op = cm.curOp, group = op.ownsGroup;
2110 if (!group) return; 3060 if (!group) return;
2111 3061
2112 try { fireCallbacksForOps(group); } 3062 try { fireCallbacksForOps(group); }
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
2162 // updateDisplay_W2 will use these properties to do the actual resizing 3112 // updateDisplay_W2 will use these properties to do the actual resizing
2163 if (display.maxLineChanged && !cm.options.lineWrapping) { 3113 if (display.maxLineChanged && !cm.options.lineWrapping) {
2164 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.l ength).left + 3; 3114 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.l ength).left + 3;
2165 cm.display.sizerWidth = op.adjustWidthTo; 3115 cm.display.sizerWidth = op.adjustWidthTo;
2166 op.barMeasure.scrollWidth = 3116 op.barMeasure.scrollWidth =
2167 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adj ustWidthTo + scrollGap(cm) + cm.display.barWidth); 3117 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adj ustWidthTo + scrollGap(cm) + cm.display.barWidth);
2168 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); 3118 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
2169 } 3119 }
2170 3120
2171 if (op.updatedDisplay || op.selectionChanged) 3121 if (op.updatedDisplay || op.selectionChanged)
2172 op.newSelectionNodes = drawSelection(cm); 3122 op.preparedSelection = display.input.prepareSelection(op.focus);
2173 } 3123 }
2174 3124
2175 function endOperation_W2(op) { 3125 function endOperation_W2(op) {
2176 var cm = op.cm; 3126 var cm = op.cm;
2177 3127
2178 if (op.adjustWidthTo != null) { 3128 if (op.adjustWidthTo != null) {
2179 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; 3129 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
2180 if (op.maxScrollLeft < cm.doc.scrollLeft) 3130 if (op.maxScrollLeft < cm.doc.scrollLeft)
2181 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollL eft), true); 3131 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollL eft), true);
2182 cm.display.maxLineChanged = false; 3132 cm.display.maxLineChanged = false;
2183 } 3133 }
2184 3134
2185 if (op.newSelectionNodes) 3135 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus | | document.hasFocus())
2186 showSelection(cm, op.newSelectionNodes); 3136 if (op.preparedSelection)
3137 cm.display.input.showSelection(op.preparedSelection, takeFocus);
3138 if (op.updatedDisplay || op.startHeight != cm.doc.height)
3139 updateScrollbars(cm, op.barMeasure);
2187 if (op.updatedDisplay) 3140 if (op.updatedDisplay)
2188 setDocumentHeight(cm, op.barMeasure); 3141 setDocumentHeight(cm, op.barMeasure);
2189 if (op.updatedDisplay || op.startHeight != cm.doc.height)
2190 updateScrollbars(cm, op.barMeasure);
2191 3142
2192 if (op.selectionChanged) restartBlink(cm); 3143 if (op.selectionChanged) restartBlink(cm);
2193 3144
2194 if (cm.state.focused && op.updateInput) 3145 if (cm.state.focused && op.updateInput)
2195 resetInput(cm, op.typing); 3146 cm.display.input.reset(op.typing);
3147 if (takeFocus) ensureFocus(op.cm);
2196 } 3148 }
2197 3149
2198 function endOperation_finish(op) { 3150 function endOperation_finish(op) {
2199 var cm = op.cm, display = cm.display, doc = cm.doc; 3151 var cm = op.cm, display = cm.display, doc = cm.doc;
2200 3152
2201 if (op.updatedDisplay) postUpdateDisplay(cm, op.update); 3153 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
2202 3154
2203 // Abort mouse wheel delta measurement, when scrolling explicitly 3155 // Abort mouse wheel delta measurement, when scrolling explicitly
2204 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) 3156 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
2205 display.wheelStartX = display.wheelStartY = null; 3157 display.wheelStartX = display.wheelStartY = null;
2206 3158
2207 // Propagate the scroll position to the actual DOM scroller 3159 // Propagate the scroll position to the actual DOM scroller
2208 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || o p.forceScroll)) { 3160 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || o p.forceScroll)) {
2209 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - displ ay.scroller.clientHeight, op.scrollTop)); 3161 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - displ ay.scroller.clientHeight, op.scrollTop));
2210 display.scrollbars.setScrollTop(doc.scrollTop); 3162 display.scrollbars.setScrollTop(doc.scrollTop);
2211 display.scroller.scrollTop = doc.scrollTop; 3163 display.scroller.scrollTop = doc.scrollTop;
2212 } 3164 }
2213 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft | | op.forceScroll)) { 3165 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft | | op.forceScroll)) {
2214 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displ ayWidth(cm), op.scrollLeft)); 3166 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displ ay.scroller.clientWidth, op.scrollLeft));
2215 display.scrollbars.setScrollLeft(doc.scrollLeft); 3167 display.scrollbars.setScrollLeft(doc.scrollLeft);
2216 display.scroller.scrollLeft = doc.scrollLeft; 3168 display.scroller.scrollLeft = doc.scrollLeft;
2217 alignHorizontally(cm); 3169 alignHorizontally(cm);
2218 } 3170 }
2219 // If we need to scroll a specific position into view, do so. 3171 // If we need to scroll a specific position into view, do so.
2220 if (op.scrollToPos) { 3172 if (op.scrollToPos) {
2221 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), 3173 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
2222 clipPos(doc, op.scrollToPos.to), op.scrollT oPos.margin); 3174 clipPos(doc, op.scrollToPos.to), op.scrollT oPos.margin);
2223 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coo rds); 3175 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coo rds);
2224 } 3176 }
2225 3177
2226 // Fire events for markers that are hidden/unidden by editing or 3178 // Fire events for markers that are hidden/unidden by editing or
2227 // undoing 3179 // undoing
2228 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; 3180 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
2229 if (hidden) for (var i = 0; i < hidden.length; ++i) 3181 if (hidden) for (var i = 0; i < hidden.length; ++i)
2230 if (!hidden[i].lines.length) signal(hidden[i], "hide"); 3182 if (!hidden[i].lines.length) signal(hidden[i], "hide");
2231 if (unhidden) for (var i = 0; i < unhidden.length; ++i) 3183 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
2232 if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); 3184 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
2233 3185
2234 if (display.wrapper.offsetHeight) 3186 if (display.wrapper.offsetHeight)
2235 doc.scrollTop = cm.display.scroller.scrollTop; 3187 doc.scrollTop = cm.display.scroller.scrollTop;
2236 3188
2237 // Fire change events, and delayed event handlers 3189 // Fire change events, and delayed event handlers
2238 if (op.changeObjs) 3190 if (op.changeObjs)
2239 signal(cm, "changes", cm, op.changeObjs); 3191 signal(cm, "changes", cm, op.changeObjs);
3192 if (op.update)
3193 op.update.finish();
2240 } 3194 }
2241 3195
2242 // Run the given function in an operation 3196 // Run the given function in an operation
2243 function runInOp(cm, f) { 3197 function runInOp(cm, f) {
2244 if (cm.curOp) return f(); 3198 if (cm.curOp) return f();
2245 startOperation(cm); 3199 startOperation(cm);
2246 try { return f(); } 3200 try { return f(); }
2247 finally { endOperation(cm); } 3201 finally { endOperation(cm); }
2248 } 3202 }
2249 // Wraps a function in an operation. Returns the wrapped function. 3203 // Wraps a function in an operation. Returns the wrapped function.
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
2455 // out of date (or nonexistent). 3409 // out of date (or nonexistent).
2456 function countDirtyView(cm) { 3410 function countDirtyView(cm) {
2457 var view = cm.display.view, dirty = 0; 3411 var view = cm.display.view, dirty = 0;
2458 for (var i = 0; i < view.length; i++) { 3412 for (var i = 0; i < view.length; i++) {
2459 var lineView = view[i]; 3413 var lineView = view[i];
2460 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty; 3414 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
2461 } 3415 }
2462 return dirty; 3416 return dirty;
2463 } 3417 }
2464 3418
2465 // INPUT HANDLING
2466
2467 // Poll for input changes, using the normal rate of polling. This
2468 // runs as long as the editor is focused.
2469 function slowPoll(cm) {
2470 if (cm.display.pollingFast) return;
2471 cm.display.poll.set(cm.options.pollInterval, function() {
2472 readInput(cm);
2473 if (cm.state.focused) slowPoll(cm);
2474 });
2475 }
2476
2477 // When an event has just come in that is likely to add or change
2478 // something in the input textarea, we poll faster, to ensure that
2479 // the change appears on the screen quickly.
2480 function fastPoll(cm) {
2481 var missed = false;
2482 cm.display.pollingFast = true;
2483 function p() {
2484 var changed = readInput(cm);
2485 if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
2486 else {cm.display.pollingFast = false; slowPoll(cm);}
2487 }
2488 cm.display.poll.set(20, p);
2489 }
2490
2491 // This will be set to an array of strings when copying, so that,
2492 // when pasting, we know what kind of selections the copied text
2493 // was made out of.
2494 var lastCopied = null;
2495
2496 // Read input from the textarea, and update the document to match.
2497 // When something is selected, it is present in the textarea, and
2498 // selected (unless it is huge, in which case a placeholder is
2499 // used). When nothing is selected, the cursor sits after previously
2500 // seen text (can be empty), which is stored in prevInput (we must
2501 // not reset the textarea when typing, because that breaks IME).
2502 function readInput(cm) {
2503 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc ;
2504 // Since this is called a *lot*, try to bail out as cheaply as
2505 // possible when it is clear that nothing happened. hasSelection
2506 // will be the case when there is a lot of text in the textarea,
2507 // in which case reading its value would be expensive.
2508 if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(c m) || cm.options.disableInput || cm.state.keySeq)
2509 return false;
2510 // See paste handler for more on the fakedLastChar kludge
2511 if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
2512 input.value = input.value.substring(0, input.value.length - 1);
2513 cm.state.fakedLastChar = false;
2514 }
2515 var text = input.value;
2516 // If nothing changed, bail.
2517 if (text == prevInput && !cm.somethingSelected()) return false;
2518 // Work around nonsensical selection resetting in IE9/10, and
2519 // inexplicable appearance of private area unicode characters on
2520 // some key combos in Mac (#2689).
2521 if (ie && ie_version >= 9 && cm.display.inputHasSelection === text ||
2522 mac && /[\uf700-\uf7ff]/.test(text)) {
2523 resetInput(cm);
2524 return false;
2525 }
2526
2527 var withOp = !cm.curOp;
2528 if (withOp) startOperation(cm);
2529 cm.display.shift = false;
2530
2531 if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput)
2532 prevInput = "\u200b";
2533 // Find the part of the input that is actually new
2534 var same = 0, l = Math.min(prevInput.length, text.length);
2535 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++sa me;
2536 var inserted = text.slice(same), textLines = splitLines(inserted);
2537
2538 // When pasing N lines into N selections, insert one line per selection
2539 var multiPaste = null;
2540 if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) {
2541 if (lastCopied && lastCopied.join("\n") == inserted)
2542 multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastC opied, splitLines);
2543 else if (textLines.length == doc.sel.ranges.length)
2544 multiPaste = map(textLines, function(l) { return [l]; });
2545 }
2546
2547 // Normal behavior is to insert the new text into every selection
2548 for (var i = doc.sel.ranges.length - 1; i >= 0; i--) {
2549 var range = doc.sel.ranges[i];
2550 var from = range.from(), to = range.to();
2551 // Handle deletion
2552 if (same < prevInput.length)
2553 from = Pos(from.line, from.ch - (prevInput.length - same));
2554 // Handle overwrite
2555 else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
2556 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + ls t(textLines).length));
2557 var updateInput = cm.curOp.updateInput;
2558 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % m ultiPaste.length] : textLines,
2559 origin: cm.state.pasteIncoming ? "paste" : cm.state.cut Incoming ? "cut" : "+input"};
2560 makeChange(cm.doc, changeEvent);
2561 signalLater(cm, "inputRead", cm, changeEvent);
2562 // When an 'electric' character is inserted, immediately trigger a reinden t
2563 if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
2564 cm.options.smartIndent && range.head.ch < 100 &&
2565 (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
2566 var mode = cm.getModeAt(range.head);
2567 var end = changeEnd(changeEvent);
2568 if (mode.electricChars) {
2569 for (var j = 0; j < mode.electricChars.length; j++)
2570 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
2571 indentLine(cm, end.line, "smart");
2572 break;
2573 }
2574 } else if (mode.electricInput) {
2575 if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.c h)))
2576 indentLine(cm, end.line, "smart");
2577 }
2578 }
2579 }
2580 ensureCursorVisible(cm);
2581 cm.curOp.updateInput = updateInput;
2582 cm.curOp.typing = true;
2583
2584 // Don't leave long text in the textarea, since it makes further polling slo w
2585 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display. prevInput = "";
2586 else cm.display.prevInput = text;
2587 if (withOp) endOperation(cm);
2588 cm.state.pasteIncoming = cm.state.cutIncoming = false;
2589 return true;
2590 }
2591
2592 // Reset the input to correspond to the selection (or to be empty,
2593 // when not typing and nothing is selected)
2594 function resetInput(cm, typing) {
2595 if (cm.display.contextMenuPending) return;
2596 var minimal, selected, doc = cm.doc;
2597 if (cm.somethingSelected()) {
2598 cm.display.prevInput = "";
2599 var range = doc.sel.primary();
2600 minimal = hasCopyEvent &&
2601 (range.to().line - range.from().line > 100 || (selected = cm.getSelectio n()).length > 1000);
2602 var content = minimal ? "-" : selected || cm.getSelection();
2603 cm.display.input.value = content;
2604 if (cm.state.focused) selectInput(cm.display.input);
2605 if (ie && ie_version >= 9) cm.display.inputHasSelection = content;
2606 } else if (!typing) {
2607 cm.display.prevInput = cm.display.input.value = "";
2608 if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
2609 }
2610 cm.display.inaccurateSelection = minimal;
2611 }
2612
2613 function focusInput(cm) {
2614 if (cm.options.readOnly != "nocursor" && (!mobile || activeElt() != cm.displ ay.input))
2615 cm.display.input.focus();
2616 }
2617
2618 function ensureFocus(cm) {
2619 if (!cm.state.focused) { focusInput(cm); onFocus(cm); }
2620 }
2621
2622 function isReadOnly(cm) {
2623 return cm.options.readOnly || cm.doc.cantEdit;
2624 }
2625
2626 // EVENT HANDLERS 3419 // EVENT HANDLERS
2627 3420
2628 // Attach the necessary event handlers when initializing the editor 3421 // Attach the necessary event handlers when initializing the editor
2629 function registerEventHandlers(cm) { 3422 function registerEventHandlers(cm) {
2630 var d = cm.display; 3423 var d = cm.display;
2631 on(d.scroller, "mousedown", operation(cm, onMouseDown)); 3424 on(d.scroller, "mousedown", operation(cm, onMouseDown));
2632 // Older IE's will not fire a second mousedown for a double click 3425 // Older IE's will not fire a second mousedown for a double click
2633 if (ie && ie_version < 11) 3426 if (ie && ie_version < 11)
2634 on(d.scroller, "dblclick", operation(cm, function(e) { 3427 on(d.scroller, "dblclick", operation(cm, function(e) {
2635 if (signalDOMEvent(cm, e)) return; 3428 if (signalDOMEvent(cm, e)) return;
2636 var pos = posFromMouse(cm, e); 3429 var pos = posFromMouse(cm, e);
2637 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return ; 3430 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return ;
2638 e_preventDefault(e); 3431 e_preventDefault(e);
2639 var word = cm.findWordAt(pos); 3432 var word = cm.findWordAt(pos);
2640 extendSelection(cm.doc, word.anchor, word.head); 3433 extendSelection(cm.doc, word.anchor, word.head);
2641 })); 3434 }));
2642 else 3435 else
2643 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preven tDefault(e); }); 3436 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preven tDefault(e); });
2644 // Prevent normal selection in the editor (we handle our own)
2645 on(d.lineSpace, "selectstart", function(e) {
2646 if (!eventInWidget(d, e)) e_preventDefault(e);
2647 });
2648 // Some browsers fire contextmenu *after* opening the menu, at 3437 // Some browsers fire contextmenu *after* opening the menu, at
2649 // which point we can't mess with it anymore. Context menu is 3438 // which point we can't mess with it anymore. Context menu is
2650 // handled in onMouseDown for these browsers. 3439 // handled in onMouseDown for these browsers.
2651 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContext Menu(cm, e);}); 3440 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContext Menu(cm, e);});
2652 3441
3442 // Used to suppress mouse event handling when a touch happens
3443 var touchFinished, prevTouch = {end: 0};
3444 function finishTouch() {
3445 if (d.activeTouch) {
3446 touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
3447 prevTouch = d.activeTouch;
3448 prevTouch.end = +new Date;
3449 }
3450 };
3451 function isMouseLikeTouchEvent(e) {
3452 if (e.touches.length != 1) return false;
3453 var touch = e.touches[0];
3454 return touch.radiusX <= 1 && touch.radiusY <= 1;
3455 }
3456 function farAway(touch, other) {
3457 if (other.left == null) return true;
3458 var dx = other.left - touch.left, dy = other.top - touch.top;
3459 return dx * dx + dy * dy > 20 * 20;
3460 }
3461 on(d.scroller, "touchstart", function(e) {
3462 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
3463 clearTimeout(touchFinished);
3464 var now = +new Date;
3465 d.activeTouch = {start: now, moved: false,
3466 prev: now - prevTouch.end <= 300 ? prevTouch : null};
3467 if (e.touches.length == 1) {
3468 d.activeTouch.left = e.touches[0].pageX;
3469 d.activeTouch.top = e.touches[0].pageY;
3470 }
3471 }
3472 });
3473 on(d.scroller, "touchmove", function() {
3474 if (d.activeTouch) d.activeTouch.moved = true;
3475 });
3476 on(d.scroller, "touchend", function(e) {
3477 var touch = d.activeTouch;
3478 if (touch && !eventInWidget(d, e) && touch.left != null &&
3479 !touch.moved && new Date - touch.start < 300) {
3480 var pos = cm.coordsChar(d.activeTouch, "page"), range;
3481 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
3482 range = new Range(pos, pos);
3483 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
3484 range = cm.findWordAt(pos);
3485 else // Triple tap
3486 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
3487 cm.setSelection(range.anchor, range.head);
3488 cm.focus();
3489 e_preventDefault(e);
3490 }
3491 finishTouch();
3492 });
3493 on(d.scroller, "touchcancel", finishTouch);
3494
2653 // Sync scrolling between fake scrollbars and real scrollable 3495 // Sync scrolling between fake scrollbars and real scrollable
2654 // area, ensure viewport is updated when scrolling. 3496 // area, ensure viewport is updated when scrolling.
2655 on(d.scroller, "scroll", function() { 3497 on(d.scroller, "scroll", function() {
2656 if (d.scroller.clientHeight) { 3498 if (d.scroller.clientHeight) {
2657 setScrollTop(cm, d.scroller.scrollTop); 3499 setScrollTop(cm, d.scroller.scrollTop);
2658 setScrollLeft(cm, d.scroller.scrollLeft, true); 3500 setScrollLeft(cm, d.scroller.scrollLeft, true);
2659 signal(cm, "scroll", cm); 3501 signal(cm, "scroll", cm);
2660 } 3502 }
2661 }); 3503 });
2662 3504
2663 // Listen to wheel events in order to try and update the viewport on time. 3505 // Listen to wheel events in order to try and update the viewport on time.
2664 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); 3506 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
2665 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); 3507 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
2666 3508
2667 // Prevent wrapper from ever scrolling 3509 // Prevent wrapper from ever scrolling
2668 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL eft = 0; }); 3510 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL eft = 0; });
2669 3511
2670 on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); }); 3512 d.dragFunctions = {
2671 on(d.input, "input", function() { 3513 enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
2672 if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inpu tHasSelection = null; 3514 over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop (e); }},
2673 readInput(cm); 3515 start: function(e){onDragStart(cm, e);},
2674 }); 3516 drop: operation(cm, onDrop),
2675 on(d.input, "keydown", operation(cm, onKeyDown)); 3517 leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
2676 on(d.input, "keypress", operation(cm, onKeyPress)); 3518 };
2677 on(d.input, "focus", bind(onFocus, cm));
2678 on(d.input, "blur", bind(onBlur, cm));
2679 3519
2680 function drag_(e) { 3520 var inp = d.input.getField();
2681 if (!signalDOMEvent(cm, e)) e_stop(e); 3521 on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
3522 on(inp, "keydown", operation(cm, onKeyDown));
3523 on(inp, "keypress", operation(cm, onKeyPress));
3524 on(inp, "focus", bind(onFocus, cm));
3525 on(inp, "blur", bind(onBlur, cm));
3526 }
3527
3528 function dragDropChanged(cm, value, old) {
3529 var wasOn = old && old != CodeMirror.Init;
3530 if (!value != !wasOn) {
3531 var funcs = cm.display.dragFunctions;
3532 var toggle = value ? on : off;
3533 toggle(cm.display.scroller, "dragstart", funcs.start);
3534 toggle(cm.display.scroller, "dragenter", funcs.enter);
3535 toggle(cm.display.scroller, "dragover", funcs.over);
3536 toggle(cm.display.scroller, "dragleave", funcs.leave);
3537 toggle(cm.display.scroller, "drop", funcs.drop);
2682 } 3538 }
2683 if (cm.options.dragDrop) {
2684 on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
2685 on(d.scroller, "dragenter", drag_);
2686 on(d.scroller, "dragover", drag_);
2687 on(d.scroller, "drop", operation(cm, onDrop));
2688 }
2689 on(d.scroller, "paste", function(e) {
2690 if (eventInWidget(d, e)) return;
2691 cm.state.pasteIncoming = true;
2692 focusInput(cm);
2693 fastPoll(cm);
2694 });
2695 on(d.input, "paste", function() {
2696 // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
2697 // Add a char to the end of textarea before paste occur so that
2698 // selection doesn't span to the end of textarea.
2699 if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleD own < 200)) {
2700 var start = d.input.selectionStart, end = d.input.selectionEnd;
2701 d.input.value += "$";
2702 // The selection end needs to be set before the start, otherwise there
2703 // can be an intermediate non-empty selection between the two, which
2704 // can override the middle-click paste buffer on linux and cause the
2705 // wrong thing to get pasted.
2706 d.input.selectionEnd = end;
2707 d.input.selectionStart = start;
2708 cm.state.fakedLastChar = true;
2709 }
2710 cm.state.pasteIncoming = true;
2711 fastPoll(cm);
2712 });
2713
2714 function prepareCopyCut(e) {
2715 if (cm.somethingSelected()) {
2716 lastCopied = cm.getSelections();
2717 if (d.inaccurateSelection) {
2718 d.prevInput = "";
2719 d.inaccurateSelection = false;
2720 d.input.value = lastCopied.join("\n");
2721 selectInput(d.input);
2722 }
2723 } else {
2724 var text = [], ranges = [];
2725 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
2726 var line = cm.doc.sel.ranges[i].head.line;
2727 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
2728 ranges.push(lineRange);
2729 text.push(cm.getRange(lineRange.anchor, lineRange.head));
2730 }
2731 if (e.type == "cut") {
2732 cm.setSelections(ranges, null, sel_dontScroll);
2733 } else {
2734 d.prevInput = "";
2735 d.input.value = text.join("\n");
2736 selectInput(d.input);
2737 }
2738 lastCopied = text;
2739 }
2740 if (e.type == "cut") cm.state.cutIncoming = true;
2741 }
2742 on(d.input, "cut", prepareCopyCut);
2743 on(d.input, "copy", prepareCopyCut);
2744
2745 // Needed to handle Tab key in KHTML
2746 if (khtml) on(d.sizer, "mouseup", function() {
2747 if (activeElt() == d.input) d.input.blur();
2748 focusInput(cm);
2749 });
2750 } 3539 }
2751 3540
2752 // Called when the window resizes 3541 // Called when the window resizes
2753 function onResize(cm) { 3542 function onResize(cm) {
2754 var d = cm.display; 3543 var d = cm.display;
2755 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapp er.clientWidth) 3544 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapp er.clientWidth)
2756 return; 3545 return;
2757 // Might be a text scaling operation, clear size caches. 3546 // Might be a text scaling operation, clear size caches.
2758 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; 3547 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
2759 d.scrollbarsClipped = false; 3548 d.scrollbarsClipped = false;
(...skipping 11 matching lines...) Expand all
2771 } 3560 }
2772 } 3561 }
2773 3562
2774 // Given a mouse event, find the corresponding position. If liberal 3563 // Given a mouse event, find the corresponding position. If liberal
2775 // is false, it checks whether a gutter or scrollbar was clicked, 3564 // is false, it checks whether a gutter or scrollbar was clicked,
2776 // and returns null if it was. forRect is used by rectangular 3565 // and returns null if it was. forRect is used by rectangular
2777 // selections, and tries to estimate a character position even for 3566 // selections, and tries to estimate a character position even for
2778 // coordinates beyond the right of the text. 3567 // coordinates beyond the right of the text.
2779 function posFromMouse(cm, e, liberal, forRect) { 3568 function posFromMouse(cm, e, liberal, forRect) {
2780 var display = cm.display; 3569 var display = cm.display;
2781 if (!liberal && e_target(e).getAttribute("not-content") == "true") return nu ll; 3570 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
2782 3571
2783 var x, y, space = display.lineSpace.getBoundingClientRect(); 3572 var x, y, space = display.lineSpace.getBoundingClientRect();
2784 // Fails unpredictably on IE[67] when mouse is dragged around quickly. 3573 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
2785 try { x = e.clientX - space.left; y = e.clientY - space.top; } 3574 try { x = e.clientX - space.left; y = e.clientY - space.top; }
2786 catch (e) { return null; } 3575 catch (e) { return null; }
2787 var coords = coordsChar(cm, x, y), line; 3576 var coords = coordsChar(cm, x, y), line;
2788 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text ).length == coords.ch) { 3577 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text ).length == coords.ch) {
2789 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.le ngth; 3578 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.le ngth;
2790 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display) .left) / charWidth(cm.display)) - colDiff)); 3579 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display) .left) / charWidth(cm.display)) - colDiff));
2791 } 3580 }
2792 return coords; 3581 return coords;
2793 } 3582 }
2794 3583
2795 // A mouse down can be a single click, double click, triple click, 3584 // A mouse down can be a single click, double click, triple click,
2796 // start of selection drag, start of text drag, new cursor 3585 // start of selection drag, start of text drag, new cursor
2797 // (ctrl-click), rectangle drag (alt-drag), or xwin 3586 // (ctrl-click), rectangle drag (alt-drag), or xwin
2798 // middle-click-paste. Or it might be a click on something we should 3587 // middle-click-paste. Or it might be a click on something we should
2799 // not interfere with, such as a scrollbar or widget. 3588 // not interfere with, such as a scrollbar or widget.
2800 function onMouseDown(e) { 3589 function onMouseDown(e) {
2801 if (signalDOMEvent(this, e)) return;
2802 var cm = this, display = cm.display; 3590 var cm = this, display = cm.display;
3591 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTo uch()) return;
2803 display.shift = e.shiftKey; 3592 display.shift = e.shiftKey;
2804 3593
2805 if (eventInWidget(display, e)) { 3594 if (eventInWidget(display, e)) {
2806 if (!webkit) { 3595 if (!webkit) {
2807 // Briefly turn off draggability, to allow widgets to do 3596 // Briefly turn off draggability, to allow widgets to do
2808 // normal dragging things. 3597 // normal dragging things.
2809 display.scroller.draggable = false; 3598 display.scroller.draggable = false;
2810 setTimeout(function(){display.scroller.draggable = true;}, 100); 3599 setTimeout(function(){display.scroller.draggable = true;}, 100);
2811 } 3600 }
2812 return; 3601 return;
2813 } 3602 }
2814 if (clickInGutter(cm, e)) return; 3603 if (clickInGutter(cm, e)) return;
2815 var start = posFromMouse(cm, e); 3604 var start = posFromMouse(cm, e);
2816 window.focus(); 3605 window.focus();
2817 3606
2818 switch (e_button(e)) { 3607 switch (e_button(e)) {
2819 case 1: 3608 case 1:
2820 if (start) 3609 // #3261: make sure, that we're not starting a second selection
3610 if (cm.state.selectingText)
3611 cm.state.selectingText(e);
3612 else if (start)
2821 leftButtonDown(cm, e, start); 3613 leftButtonDown(cm, e, start);
2822 else if (e_target(e) == display.scroller) 3614 else if (e_target(e) == display.scroller)
2823 e_preventDefault(e); 3615 e_preventDefault(e);
2824 break; 3616 break;
2825 case 2: 3617 case 2:
2826 if (webkit) cm.state.lastMiddleDown = +new Date; 3618 if (webkit) cm.state.lastMiddleDown = +new Date;
2827 if (start) extendSelection(cm.doc, start); 3619 if (start) extendSelection(cm.doc, start);
2828 setTimeout(bind(focusInput, cm), 20); 3620 setTimeout(function() {display.input.focus();}, 20);
2829 e_preventDefault(e); 3621 e_preventDefault(e);
2830 break; 3622 break;
2831 case 3: 3623 case 3:
2832 if (captureRightClick) onContextMenu(cm, e); 3624 if (captureRightClick) onContextMenu(cm, e);
3625 else delayBlurEvent(cm);
2833 break; 3626 break;
2834 } 3627 }
2835 } 3628 }
2836 3629
2837 var lastClick, lastDoubleClick; 3630 var lastClick, lastDoubleClick;
2838 function leftButtonDown(cm, e, start) { 3631 function leftButtonDown(cm, e, start) {
2839 setTimeout(bind(ensureFocus, cm), 0); 3632 if (ie) setTimeout(bind(ensureFocus, cm), 0);
3633 else cm.curOp.focus = activeElt();
2840 3634
2841 var now = +new Date, type; 3635 var now = +new Date, type;
2842 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleCli ck.pos, start) == 0) { 3636 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleCli ck.pos, start) == 0) {
2843 type = "triple"; 3637 type = "triple";
2844 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, sta rt) == 0) { 3638 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, sta rt) == 0) {
2845 type = "double"; 3639 type = "double";
2846 lastDoubleClick = {time: now, pos: start}; 3640 lastDoubleClick = {time: now, pos: start};
2847 } else { 3641 } else {
2848 type = "single"; 3642 type = "single";
2849 lastClick = {time: now, pos: start}; 3643 lastClick = {time: now, pos: start};
2850 } 3644 }
2851 3645
2852 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained; 3646 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
2853 if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && 3647 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
2854 type == "single" && (contained = sel.contains(start)) > -1 && 3648 type == "single" && (contained = sel.contains(start)) > -1 &&
2855 !sel.ranges[contained].empty()) 3649 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRe l > 0) &&
3650 (cmp(contained.to(), start) > 0 || start.xRel < 0))
2856 leftButtonStartDrag(cm, e, start, modifier); 3651 leftButtonStartDrag(cm, e, start, modifier);
2857 else 3652 else
2858 leftButtonSelect(cm, e, start, type, modifier); 3653 leftButtonSelect(cm, e, start, type, modifier);
2859 } 3654 }
2860 3655
2861 // Start a text drag. When it ends, see if any dragging actually 3656 // Start a text drag. When it ends, see if any dragging actually
2862 // happen, and treat as a click if it didn't. 3657 // happen, and treat as a click if it didn't.
2863 function leftButtonStartDrag(cm, e, start, modifier) { 3658 function leftButtonStartDrag(cm, e, start, modifier) {
2864 var display = cm.display; 3659 var display = cm.display, startTime = +new Date;
2865 var dragEnd = operation(cm, function(e2) { 3660 var dragEnd = operation(cm, function(e2) {
2866 if (webkit) display.scroller.draggable = false; 3661 if (webkit) display.scroller.draggable = false;
2867 cm.state.draggingText = false; 3662 cm.state.draggingText = false;
2868 off(document, "mouseup", dragEnd); 3663 off(document, "mouseup", dragEnd);
2869 off(display.scroller, "drop", dragEnd); 3664 off(display.scroller, "drop", dragEnd);
2870 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { 3665 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
2871 e_preventDefault(e2); 3666 e_preventDefault(e2);
2872 if (!modifier) 3667 if (!modifier && +new Date - 200 < startTime)
2873 extendSelection(cm.doc, start); 3668 extendSelection(cm.doc, start);
2874 focusInput(cm); 3669 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3 081)
2875 // Work around unexplainable focus problem in IE9 (#2127) 3670 if (webkit || ie && ie_version == 9)
2876 if (ie && ie_version == 9) 3671 setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
2877 setTimeout(function() {document.body.focus(); focusInput(cm);}, 20); 3672 else
3673 display.input.focus();
2878 } 3674 }
2879 }); 3675 });
2880 // Let the drag handler handle this. 3676 // Let the drag handler handle this.
2881 if (webkit) display.scroller.draggable = true; 3677 if (webkit) display.scroller.draggable = true;
2882 cm.state.draggingText = dragEnd; 3678 cm.state.draggingText = dragEnd;
3679 dragEnd.copy = mac ? e.altKey : e.ctrlKey
2883 // IE's approach to draggable 3680 // IE's approach to draggable
2884 if (display.scroller.dragDrop) display.scroller.dragDrop(); 3681 if (display.scroller.dragDrop) display.scroller.dragDrop();
2885 on(document, "mouseup", dragEnd); 3682 on(document, "mouseup", dragEnd);
2886 on(display.scroller, "drop", dragEnd); 3683 on(display.scroller, "drop", dragEnd);
2887 } 3684 }
2888 3685
2889 // Normal selection, as opposed to text dragging. 3686 // Normal selection, as opposed to text dragging.
2890 function leftButtonSelect(cm, e, start, type, addNew) { 3687 function leftButtonSelect(cm, e, start, type, addNew) {
2891 var display = cm.display, doc = cm.doc; 3688 var display = cm.display, doc = cm.doc;
2892 e_preventDefault(e); 3689 e_preventDefault(e);
2893 3690
2894 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; 3691 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
2895 if (addNew && !e.shiftKey) { 3692 if (addNew && !e.shiftKey) {
2896 ourIndex = doc.sel.contains(start); 3693 ourIndex = doc.sel.contains(start);
2897 if (ourIndex > -1) 3694 if (ourIndex > -1)
2898 ourRange = ranges[ourIndex]; 3695 ourRange = ranges[ourIndex];
2899 else 3696 else
2900 ourRange = new Range(start, start); 3697 ourRange = new Range(start, start);
2901 } else { 3698 } else {
2902 ourRange = doc.sel.primary(); 3699 ourRange = doc.sel.primary();
3700 ourIndex = doc.sel.primIndex;
2903 } 3701 }
2904 3702
2905 if (e.altKey) { 3703 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
2906 type = "rect"; 3704 type = "rect";
2907 if (!addNew) ourRange = new Range(start, start); 3705 if (!addNew) ourRange = new Range(start, start);
2908 start = posFromMouse(cm, e, true, true); 3706 start = posFromMouse(cm, e, true, true);
2909 ourIndex = -1; 3707 ourIndex = -1;
2910 } else if (type == "double") { 3708 } else if (type == "double") {
2911 var word = cm.findWordAt(start); 3709 var word = cm.findWordAt(start);
2912 if (cm.display.shift || doc.extend) 3710 if (cm.display.shift || doc.extend)
2913 ourRange = extendRange(doc, ourRange, word.anchor, word.head); 3711 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
2914 else 3712 else
2915 ourRange = word; 3713 ourRange = word;
2916 } else if (type == "triple") { 3714 } else if (type == "triple") {
2917 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))); 3715 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
2918 if (cm.display.shift || doc.extend) 3716 if (cm.display.shift || doc.extend)
2919 ourRange = extendRange(doc, ourRange, line.anchor, line.head); 3717 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
2920 else 3718 else
2921 ourRange = line; 3719 ourRange = line;
2922 } else { 3720 } else {
2923 ourRange = extendRange(doc, ourRange, start); 3721 ourRange = extendRange(doc, ourRange, start);
2924 } 3722 }
2925 3723
2926 if (!addNew) { 3724 if (!addNew) {
2927 ourIndex = 0; 3725 ourIndex = 0;
2928 setSelection(doc, new Selection([ourRange], 0), sel_mouse); 3726 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
2929 startSel = doc.sel; 3727 startSel = doc.sel;
2930 } else if (ourIndex == -1) { 3728 } else if (ourIndex == -1) {
2931 ourIndex = ranges.length; 3729 ourIndex = ranges.length;
2932 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), 3730 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
2933 {scroll: false, origin: "*mouse"}); 3731 {scroll: false, origin: "*mouse"});
2934 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" ) { 3732 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
2935 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(rang es.slice(ourIndex + 1)), 0)); 3733 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(rang es.slice(ourIndex + 1)), 0),
3734 {scroll: false, origin: "*mouse"});
2936 startSel = doc.sel; 3735 startSel = doc.sel;
2937 } else { 3736 } else {
2938 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); 3737 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
2939 } 3738 }
2940 3739
2941 var lastPos = start; 3740 var lastPos = start;
2942 function extendTo(pos) { 3741 function extendTo(pos) {
2943 if (cmp(lastPos, pos) == 0) return; 3742 if (cmp(lastPos, pos) == 0) return;
2944 lastPos = pos; 3743 lastPos = pos;
2945 3744
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
2987 // happened in the meantime (clearTimeout isn't reliable -- at 3786 // happened in the meantime (clearTimeout isn't reliable -- at
2988 // least on Chrome, the timeouts still happen even when cleared, 3787 // least on Chrome, the timeouts still happen even when cleared,
2989 // if the clear happens after their scheduled firing time). 3788 // if the clear happens after their scheduled firing time).
2990 var counter = 0; 3789 var counter = 0;
2991 3790
2992 function extend(e) { 3791 function extend(e) {
2993 var curCount = ++counter; 3792 var curCount = ++counter;
2994 var cur = posFromMouse(cm, e, true, type == "rect"); 3793 var cur = posFromMouse(cm, e, true, type == "rect");
2995 if (!cur) return; 3794 if (!cur) return;
2996 if (cmp(cur, lastPos) != 0) { 3795 if (cmp(cur, lastPos) != 0) {
2997 ensureFocus(cm); 3796 cm.curOp.focus = activeElt();
2998 extendTo(cur); 3797 extendTo(cur);
2999 var visible = visibleLines(display, doc); 3798 var visible = visibleLines(display, doc);
3000 if (cur.line >= visible.to || cur.line < visible.from) 3799 if (cur.line >= visible.to || cur.line < visible.from)
3001 setTimeout(operation(cm, function(){if (counter == curCount) extend(e) ;}), 150); 3800 setTimeout(operation(cm, function(){if (counter == curCount) extend(e) ;}), 150);
3002 } else { 3801 } else {
3003 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize. bottom ? 20 : 0; 3802 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize. bottom ? 20 : 0;
3004 if (outside) setTimeout(operation(cm, function() { 3803 if (outside) setTimeout(operation(cm, function() {
3005 if (counter != curCount) return; 3804 if (counter != curCount) return;
3006 display.scroller.scrollTop += outside; 3805 display.scroller.scrollTop += outside;
3007 extend(e); 3806 extend(e);
3008 }), 50); 3807 }), 50);
3009 } 3808 }
3010 } 3809 }
3011 3810
3012 function done(e) { 3811 function done(e) {
3812 cm.state.selectingText = false;
3013 counter = Infinity; 3813 counter = Infinity;
3014 e_preventDefault(e); 3814 e_preventDefault(e);
3015 focusInput(cm); 3815 display.input.focus();
3016 off(document, "mousemove", move); 3816 off(document, "mousemove", move);
3017 off(document, "mouseup", up); 3817 off(document, "mouseup", up);
3018 doc.history.lastSelOrigin = null; 3818 doc.history.lastSelOrigin = null;
3019 } 3819 }
3020 3820
3021 var move = operation(cm, function(e) { 3821 var move = operation(cm, function(e) {
3022 if (!e_button(e)) done(e); 3822 if (!e_button(e)) done(e);
3023 else extend(e); 3823 else extend(e);
3024 }); 3824 });
3025 var up = operation(cm, done); 3825 var up = operation(cm, done);
3826 cm.state.selectingText = up;
3026 on(document, "mousemove", move); 3827 on(document, "mousemove", move);
3027 on(document, "mouseup", up); 3828 on(document, "mouseup", up);
3028 } 3829 }
3029 3830
3030 // Determines whether an event happened in the gutter, and fires the 3831 // Determines whether an event happened in the gutter, and fires the
3031 // handlers for the corresponding event. 3832 // handlers for the corresponding event.
3032 function gutterEvent(cm, e, type, prevent, signalfn) { 3833 function gutterEvent(cm, e, type, prevent) {
3033 try { var mX = e.clientX, mY = e.clientY; } 3834 try { var mX = e.clientX, mY = e.clientY; }
3034 catch(e) { return false; } 3835 catch(e) { return false; }
3035 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) retu rn false; 3836 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) retu rn false;
3036 if (prevent) e_preventDefault(e); 3837 if (prevent) e_preventDefault(e);
3037 3838
3038 var display = cm.display; 3839 var display = cm.display;
3039 var lineBox = display.lineDiv.getBoundingClientRect(); 3840 var lineBox = display.lineDiv.getBoundingClientRect();
3040 3841
3041 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented( e); 3842 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented( e);
3042 mY -= lineBox.top - display.viewOffset; 3843 mY -= lineBox.top - display.viewOffset;
3043 3844
3044 for (var i = 0; i < cm.options.gutters.length; ++i) { 3845 for (var i = 0; i < cm.options.gutters.length; ++i) {
3045 var g = display.gutters.childNodes[i]; 3846 var g = display.gutters.childNodes[i];
3046 if (g && g.getBoundingClientRect().right >= mX) { 3847 if (g && g.getBoundingClientRect().right >= mX) {
3047 var line = lineAtHeight(cm.doc, mY); 3848 var line = lineAtHeight(cm.doc, mY);
3048 var gutter = cm.options.gutters[i]; 3849 var gutter = cm.options.gutters[i];
3049 signalfn(cm, type, cm, line, gutter, e); 3850 signal(cm, type, cm, line, gutter, e);
3050 return e_defaultPrevented(e); 3851 return e_defaultPrevented(e);
3051 } 3852 }
3052 } 3853 }
3053 } 3854 }
3054 3855
3055 function clickInGutter(cm, e) { 3856 function clickInGutter(cm, e) {
3056 return gutterEvent(cm, e, "gutterClick", true, signalLater); 3857 return gutterEvent(cm, e, "gutterClick", true);
3057 } 3858 }
3058 3859
3059 // Kludge to work around strange IE behavior where it'll sometimes 3860 // Kludge to work around strange IE behavior where it'll sometimes
3060 // re-fire a series of drag-related events right after the drop (#1551) 3861 // re-fire a series of drag-related events right after the drop (#1551)
3061 var lastDrop = 0; 3862 var lastDrop = 0;
3062 3863
3063 function onDrop(e) { 3864 function onDrop(e) {
3064 var cm = this; 3865 var cm = this;
3866 clearDragCursor(cm);
3065 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) 3867 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
3066 return; 3868 return;
3067 e_preventDefault(e); 3869 e_preventDefault(e);
3068 if (ie) lastDrop = +new Date; 3870 if (ie) lastDrop = +new Date;
3069 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; 3871 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
3070 if (!pos || isReadOnly(cm)) return; 3872 if (!pos || cm.isReadOnly()) return;
3071 // Might be a file drop, in which case we simply extract the text 3873 // Might be a file drop, in which case we simply extract the text
3072 // and insert it. 3874 // and insert it.
3073 if (files && files.length && window.FileReader && window.File) { 3875 if (files && files.length && window.FileReader && window.File) {
3074 var n = files.length, text = Array(n), read = 0; 3876 var n = files.length, text = Array(n), read = 0;
3075 var loadFile = function(file, i) { 3877 var loadFile = function(file, i) {
3878 if (cm.options.allowDropFileTypes &&
3879 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
3880 return;
3881
3076 var reader = new FileReader; 3882 var reader = new FileReader;
3077 reader.onload = operation(cm, function() { 3883 reader.onload = operation(cm, function() {
3078 text[i] = reader.result; 3884 var content = reader.result;
3885 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
3886 text[i] = content;
3079 if (++read == n) { 3887 if (++read == n) {
3080 pos = clipPos(cm.doc, pos); 3888 pos = clipPos(cm.doc, pos);
3081 var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}; 3889 var change = {from: pos, to: pos,
3890 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator ())),
3891 origin: "paste"};
3082 makeChange(cm.doc, change); 3892 makeChange(cm.doc, change);
3083 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(ch ange))); 3893 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(ch ange)));
3084 } 3894 }
3085 }); 3895 });
3086 reader.readAsText(file); 3896 reader.readAsText(file);
3087 }; 3897 };
3088 for (var i = 0; i < n; ++i) loadFile(files[i], i); 3898 for (var i = 0; i < n; ++i) loadFile(files[i], i);
3089 } else { // Normal drop 3899 } else { // Normal drop
3090 // Don't do a replace if the drop happened inside of the selected text. 3900 // Don't do a replace if the drop happened inside of the selected text.
3091 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { 3901 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
3092 cm.state.draggingText(e); 3902 cm.state.draggingText(e);
3093 // Ensure the editor is re-focused 3903 // Ensure the editor is re-focused
3094 setTimeout(bind(focusInput, cm), 20); 3904 setTimeout(function() {cm.display.input.focus();}, 20);
3095 return; 3905 return;
3096 } 3906 }
3097 try { 3907 try {
3098 var text = e.dataTransfer.getData("Text"); 3908 var text = e.dataTransfer.getData("Text");
3099 if (text) { 3909 if (text) {
3100 if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey)) 3910 if (cm.state.draggingText && !cm.state.draggingText.copy)
3101 var selected = cm.listSelections(); 3911 var selected = cm.listSelections();
3102 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); 3912 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
3103 if (selected) for (var i = 0; i < selected.length; ++i) 3913 if (selected) for (var i = 0; i < selected.length; ++i)
3104 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag "); 3914 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag ");
3105 cm.replaceSelection(text, "around", "paste"); 3915 cm.replaceSelection(text, "around", "paste");
3106 focusInput(cm); 3916 cm.display.input.focus();
3107 } 3917 }
3108 } 3918 }
3109 catch(e){} 3919 catch(e){}
3110 } 3920 }
3111 } 3921 }
3112 3922
3113 function onDragStart(cm, e) { 3923 function onDragStart(cm, e) {
3114 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e ); return; } 3924 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e ); return; }
3115 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; 3925 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
3116 3926
3117 e.dataTransfer.setData("Text", cm.getSelection()); 3927 e.dataTransfer.setData("Text", cm.getSelection());
3928 e.dataTransfer.effectAllowed = "copyMove"
3118 3929
3119 // Use dummy image instead of default browsers image. 3930 // Use dummy image instead of default browsers image.
3120 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. 3931 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
3121 if (e.dataTransfer.setDragImage && !safari) { 3932 if (e.dataTransfer.setDragImage && !safari) {
3122 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); 3933 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
3123 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAA AICTAEAOw=="; 3934 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAA AICTAEAOw==";
3124 if (presto) { 3935 if (presto) {
3125 img.width = img.height = 1; 3936 img.width = img.height = 1;
3126 cm.display.wrapper.appendChild(img); 3937 cm.display.wrapper.appendChild(img);
3127 // Force a relayout, or Opera won't use our image for some obscure reaso n 3938 // Force a relayout, or Opera won't use our image for some obscure reaso n
3128 img._top = img.offsetTop; 3939 img._top = img.offsetTop;
3129 } 3940 }
3130 e.dataTransfer.setDragImage(img, 0, 0); 3941 e.dataTransfer.setDragImage(img, 0, 0);
3131 if (presto) img.parentNode.removeChild(img); 3942 if (presto) img.parentNode.removeChild(img);
3132 } 3943 }
3133 } 3944 }
3134 3945
3946 function onDragOver(cm, e) {
3947 var pos = posFromMouse(cm, e);
3948 if (!pos) return;
3949 var frag = document.createDocumentFragment();
3950 drawSelectionCursor(cm, pos, frag);
3951 if (!cm.display.dragCursor) {
3952 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dr agcursors");
3953 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursor Div);
3954 }
3955 removeChildrenAndAdd(cm.display.dragCursor, frag);
3956 }
3957
3958 function clearDragCursor(cm) {
3959 if (cm.display.dragCursor) {
3960 cm.display.lineSpace.removeChild(cm.display.dragCursor);
3961 cm.display.dragCursor = null;
3962 }
3963 }
3964
3135 // SCROLL EVENTS 3965 // SCROLL EVENTS
3136 3966
3137 // Sync the scrollable area and scrollbars, ensure the viewport 3967 // Sync the scrollable area and scrollbars, ensure the viewport
3138 // covers the visible area. 3968 // covers the visible area.
3139 function setScrollTop(cm, val) { 3969 function setScrollTop(cm, val) {
3140 if (Math.abs(cm.doc.scrollTop - val) < 2) return; 3970 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
3141 cm.doc.scrollTop = val; 3971 cm.doc.scrollTop = val;
3142 if (!gecko) updateDisplaySimple(cm, {top: val}); 3972 if (!gecko) updateDisplaySimple(cm, {top: val});
3143 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = va l; 3973 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = va l;
3144 cm.display.scrollbars.setScrollTop(val); 3974 cm.display.scrollbars.setScrollTop(val);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
3189 delta.x *= wheelPixelsPerUnit; 4019 delta.x *= wheelPixelsPerUnit;
3190 delta.y *= wheelPixelsPerUnit; 4020 delta.y *= wheelPixelsPerUnit;
3191 return delta; 4021 return delta;
3192 }; 4022 };
3193 4023
3194 function onScrollWheel(cm, e) { 4024 function onScrollWheel(cm, e) {
3195 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; 4025 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
3196 4026
3197 var display = cm.display, scroll = display.scroller; 4027 var display = cm.display, scroll = display.scroller;
3198 // Quit if there's nothing to scroll here 4028 // Quit if there's nothing to scroll here
3199 if (!(dx && scroll.scrollWidth > scroll.clientWidth || 4029 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
3200 dy && scroll.scrollHeight > scroll.clientHeight)) return; 4030 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
4031 if (!(dx && canScrollX || dy && canScrollY)) return;
3201 4032
3202 // Webkit browsers on OS X abort momentum scrolls when the target 4033 // Webkit browsers on OS X abort momentum scrolls when the target
3203 // of the scroll event is removed from the scrollable element. 4034 // of the scroll event is removed from the scrollable element.
3204 // This hack (see related code in patchDisplay) makes sure the 4035 // This hack (see related code in patchDisplay) makes sure the
3205 // element is kept around. 4036 // element is kept around.
3206 if (dy && mac && webkit) { 4037 if (dy && mac && webkit) {
3207 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { 4038 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
3208 for (var i = 0; i < view.length; i++) { 4039 for (var i = 0; i < view.length; i++) {
3209 if (view[i].node == cur) { 4040 if (view[i].node == cur) {
3210 cm.display.currentWheelTarget = cur; 4041 cm.display.currentWheelTarget = cur;
3211 break outer; 4042 break outer;
3212 } 4043 }
3213 } 4044 }
3214 } 4045 }
3215 } 4046 }
3216 4047
3217 // On some browsers, horizontal scrolling will cause redraws to 4048 // On some browsers, horizontal scrolling will cause redraws to
3218 // happen before the gutter has been realigned, causing it to 4049 // happen before the gutter has been realigned, causing it to
3219 // wriggle around in a most unseemly way. When we have an 4050 // wriggle around in a most unseemly way. When we have an
3220 // estimated pixels/delta value, we just handle horizontal 4051 // estimated pixels/delta value, we just handle horizontal
3221 // scrolling entirely here. It'll be slightly off from native, but 4052 // scrolling entirely here. It'll be slightly off from native, but
3222 // better than glitching out. 4053 // better than glitching out.
3223 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { 4054 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
3224 if (dy) 4055 if (dy && canScrollY)
3225 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixel sPerUnit, scroll.scrollHeight - scroll.clientHeight))); 4056 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixel sPerUnit, scroll.scrollHeight - scroll.clientHeight)));
3226 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixel sPerUnit, scroll.scrollWidth - scroll.clientWidth))); 4057 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixel sPerUnit, scroll.scrollWidth - scroll.clientWidth)));
3227 e_preventDefault(e); 4058 // Only prevent default scrolling if vertical scrolling is
4059 // actually possible. Otherwise, it causes vertical scroll
4060 // jitter on OSX trackpads when deltaX is small and deltaY
4061 // is large (issue #3579)
4062 if (!dy || (dy && canScrollY))
4063 e_preventDefault(e);
3228 display.wheelStartX = null; // Abort measurement, if in progress 4064 display.wheelStartX = null; // Abort measurement, if in progress
3229 return; 4065 return;
3230 } 4066 }
3231 4067
3232 // 'Project' the visible viewport to cover the area that is being 4068 // 'Project' the visible viewport to cover the area that is being
3233 // scrolled into view (if we know enough to estimate it). 4069 // scrolled into view (if we know enough to estimate it).
3234 if (dy && wheelPixelsPerUnit != null) { 4070 if (dy && wheelPixelsPerUnit != null) {
3235 var pixels = dy * wheelPixelsPerUnit; 4071 var pixels = dy * wheelPixelsPerUnit;
3236 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; 4072 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
3237 if (pixels < 0) top = Math.max(0, top + pixels - 50); 4073 if (pixels < 0) top = Math.max(0, top + pixels - 50);
(...skipping 25 matching lines...) Expand all
3263 // KEY EVENTS 4099 // KEY EVENTS
3264 4100
3265 // Run a handler that was bound to a key. 4101 // Run a handler that was bound to a key.
3266 function doHandleBinding(cm, bound, dropShift) { 4102 function doHandleBinding(cm, bound, dropShift) {
3267 if (typeof bound == "string") { 4103 if (typeof bound == "string") {
3268 bound = commands[bound]; 4104 bound = commands[bound];
3269 if (!bound) return false; 4105 if (!bound) return false;
3270 } 4106 }
3271 // Ensure previous input has been read, so that the handler sees a 4107 // Ensure previous input has been read, so that the handler sees a
3272 // consistent view of the document 4108 // consistent view of the document
3273 if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false; 4109 cm.display.input.ensurePolled();
3274 var prevShift = cm.display.shift, done = false; 4110 var prevShift = cm.display.shift, done = false;
3275 try { 4111 try {
3276 if (isReadOnly(cm)) cm.state.suppressEdits = true; 4112 if (cm.isReadOnly()) cm.state.suppressEdits = true;
3277 if (dropShift) cm.display.shift = false; 4113 if (dropShift) cm.display.shift = false;
3278 done = bound(cm) != Pass; 4114 done = bound(cm) != Pass;
3279 } finally { 4115 } finally {
3280 cm.display.shift = prevShift; 4116 cm.display.shift = prevShift;
3281 cm.state.suppressEdits = false; 4117 cm.state.suppressEdits = false;
3282 } 4118 }
3283 return done; 4119 return done;
3284 } 4120 }
3285 4121
3286 function lookupKeyForEditor(cm, name, handle) { 4122 function lookupKeyForEditor(cm, name, handle) {
3287 for (var i = 0; i < cm.state.keyMaps.length; i++) { 4123 for (var i = 0; i < cm.state.keyMaps.length; i++) {
3288 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); 4124 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
3289 if (result) return result; 4125 if (result) return result;
3290 } 4126 }
3291 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle , cm)) 4127 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle , cm))
3292 || lookupKey(name, cm.options.keyMap, handle, cm); 4128 || lookupKey(name, cm.options.keyMap, handle, cm);
3293 } 4129 }
3294 4130
3295 var stopSeq = new Delayed; 4131 var stopSeq = new Delayed;
3296 function dispatchKey(cm, name, e, handle) { 4132 function dispatchKey(cm, name, e, handle) {
3297 var seq = cm.state.keySeq; 4133 var seq = cm.state.keySeq;
3298 if (seq) { 4134 if (seq) {
3299 if (isModifierKey(name)) return "handled"; 4135 if (isModifierKey(name)) return "handled";
3300 stopSeq.set(50, function() { 4136 stopSeq.set(50, function() {
3301 if (cm.state.keySeq == seq) { 4137 if (cm.state.keySeq == seq) {
3302 cm.state.keySeq = null; 4138 cm.state.keySeq = null;
3303 resetInput(cm); 4139 cm.display.input.reset();
3304 } 4140 }
3305 }); 4141 });
3306 name = seq + " " + name; 4142 name = seq + " " + name;
3307 } 4143 }
3308 var result = lookupKeyForEditor(cm, name, handle); 4144 var result = lookupKeyForEditor(cm, name, handle);
3309 4145
3310 if (result == "multi") 4146 if (result == "multi")
3311 cm.state.keySeq = name; 4147 cm.state.keySeq = name;
3312 if (result == "handled") 4148 if (result == "handled")
3313 signalLater(cm, "keyHandled", cm, name, e); 4149 signalLater(cm, "keyHandled", cm, name, e);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
3345 4181
3346 // Handle a key from the keypress event 4182 // Handle a key from the keypress event
3347 function handleCharBinding(cm, e, ch) { 4183 function handleCharBinding(cm, e, ch) {
3348 return dispatchKey(cm, "'" + ch + "'", e, 4184 return dispatchKey(cm, "'" + ch + "'", e,
3349 function(b) { return doHandleBinding(cm, b, true); }); 4185 function(b) { return doHandleBinding(cm, b, true); });
3350 } 4186 }
3351 4187
3352 var lastStoppedKey = null; 4188 var lastStoppedKey = null;
3353 function onKeyDown(e) { 4189 function onKeyDown(e) {
3354 var cm = this; 4190 var cm = this;
3355 ensureFocus(cm); 4191 cm.curOp.focus = activeElt();
3356 if (signalDOMEvent(cm, e)) return; 4192 if (signalDOMEvent(cm, e)) return;
3357 // IE does strange things with escape. 4193 // IE does strange things with escape.
3358 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false; 4194 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
3359 var code = e.keyCode; 4195 var code = e.keyCode;
3360 cm.display.shift = code == 16 || e.shiftKey; 4196 cm.display.shift = code == 16 || e.shiftKey;
3361 var handled = handleKeyBinding(cm, e); 4197 var handled = handleKeyBinding(cm, e);
3362 if (presto) { 4198 if (presto) {
3363 lastStoppedKey = handled ? code : null; 4199 lastStoppedKey = handled ? code : null;
3364 // Opera has no cut event... we try to at least catch the key combo 4200 // Opera has no cut event... we try to at least catch the key combo
3365 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKe y)) 4201 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKe y))
(...skipping 20 matching lines...) Expand all
3386 on(document, "mouseover", up); 4222 on(document, "mouseover", up);
3387 } 4223 }
3388 4224
3389 function onKeyUp(e) { 4225 function onKeyUp(e) {
3390 if (e.keyCode == 16) this.doc.sel.shift = false; 4226 if (e.keyCode == 16) this.doc.sel.shift = false;
3391 signalDOMEvent(this, e); 4227 signalDOMEvent(this, e);
3392 } 4228 }
3393 4229
3394 function onKeyPress(e) { 4230 function onKeyPress(e) {
3395 var cm = this; 4231 var cm = this;
3396 if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) ret urn; 4232 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e .altKey || mac && e.metaKey) return;
3397 var keyCode = e.keyCode, charCode = e.charCode; 4233 var keyCode = e.keyCode, charCode = e.charCode;
3398 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDe fault(e); return;} 4234 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDe fault(e); return;}
3399 if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm , e)) return; 4235 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) retur n;
3400 var ch = String.fromCharCode(charCode == null ? keyCode : charCode); 4236 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
3401 if (handleCharBinding(cm, e, ch)) return; 4237 if (handleCharBinding(cm, e, ch)) return;
3402 if (ie && ie_version >= 9) cm.display.inputHasSelection = null; 4238 cm.display.input.onKeyPress(e);
3403 fastPoll(cm);
3404 } 4239 }
3405 4240
3406 // FOCUS/BLUR EVENTS 4241 // FOCUS/BLUR EVENTS
3407 4242
4243 function delayBlurEvent(cm) {
4244 cm.state.delayingBlurEvent = true;
4245 setTimeout(function() {
4246 if (cm.state.delayingBlurEvent) {
4247 cm.state.delayingBlurEvent = false;
4248 onBlur(cm);
4249 }
4250 }, 100);
4251 }
4252
3408 function onFocus(cm) { 4253 function onFocus(cm) {
4254 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
4255
3409 if (cm.options.readOnly == "nocursor") return; 4256 if (cm.options.readOnly == "nocursor") return;
3410 if (!cm.state.focused) { 4257 if (!cm.state.focused) {
3411 signal(cm, "focus", cm); 4258 signal(cm, "focus", cm);
3412 cm.state.focused = true; 4259 cm.state.focused = true;
3413 addClass(cm.display.wrapper, "CodeMirror-focused"); 4260 addClass(cm.display.wrapper, "CodeMirror-focused");
3414 // The prevInput test prevents this from firing when a context 4261 // This test prevents this from firing when a context
3415 // menu is closed (since the resetInput would kill the 4262 // menu is closed (since the input reset would kill the
3416 // select-all detection hack) 4263 // select-all detection hack)
3417 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { 4264 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
3418 resetInput(cm); 4265 cm.display.input.reset();
3419 if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730 4266 if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20) ; // Issue #1730
3420 } 4267 }
4268 cm.display.input.receivedFocus();
3421 } 4269 }
3422 slowPoll(cm);
3423 restartBlink(cm); 4270 restartBlink(cm);
3424 } 4271 }
3425 function onBlur(cm) { 4272 function onBlur(cm) {
4273 if (cm.state.delayingBlurEvent) return;
4274
3426 if (cm.state.focused) { 4275 if (cm.state.focused) {
3427 signal(cm, "blur", cm); 4276 signal(cm, "blur", cm);
3428 cm.state.focused = false; 4277 cm.state.focused = false;
3429 rmClass(cm.display.wrapper, "CodeMirror-focused"); 4278 rmClass(cm.display.wrapper, "CodeMirror-focused");
3430 } 4279 }
3431 clearInterval(cm.display.blinker); 4280 clearInterval(cm.display.blinker);
3432 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 15 0); 4281 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 15 0);
3433 } 4282 }
3434 4283
3435 // CONTEXT MENU HANDLING 4284 // CONTEXT MENU HANDLING
3436 4285
3437 // To make the context menu work, we need to briefly unhide the 4286 // To make the context menu work, we need to briefly unhide the
3438 // textarea (making it as unobtrusive as possible) to let the 4287 // textarea (making it as unobtrusive as possible) to let the
3439 // right-click take effect on it. 4288 // right-click take effect on it.
3440 function onContextMenu(cm, e) { 4289 function onContextMenu(cm, e) {
4290 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
3441 if (signalDOMEvent(cm, e, "contextmenu")) return; 4291 if (signalDOMEvent(cm, e, "contextmenu")) return;
3442 var display = cm.display; 4292 cm.display.input.onContextMenu(e);
3443 if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
3444
3445 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
3446 if (!pos || presto) return; // Opera is difficult.
3447
3448 // Reset the current text selection only if the click is done outside of the selection
3449 // and 'resetSelectionOnContextMenu' option is true.
3450 var reset = cm.options.resetSelectionOnContextMenu;
3451 if (reset && cm.doc.sel.contains(pos) == -1)
3452 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
3453
3454 var oldCSS = display.input.style.cssText;
3455 display.inputDiv.style.position = "absolute";
3456 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; t op: " + (e.clientY - 5) +
3457 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
3458 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
3459 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacit y: .05; filter: alpha(opacity=5);";
3460 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2 712)
3461 focusInput(cm);
3462 if (webkit) window.scrollTo(null, oldScrollY);
3463 resetInput(cm);
3464 // Adds "Select all" to context menu in FF
3465 if (!cm.somethingSelected()) display.input.value = display.prevInput = " ";
3466 display.contextMenuPending = true;
3467 display.selForContextMenu = cm.doc.sel;
3468 clearTimeout(display.detectingSelectAll);
3469
3470 // Select-all will be greyed out if there's nothing to select, so
3471 // this adds a zero-width space so that we can later check whether
3472 // it got selected.
3473 function prepareSelectAllHack() {
3474 if (display.input.selectionStart != null) {
3475 var selected = cm.somethingSelected();
3476 var extval = display.input.value = "\u200b" + (selected ? display.input. value : "");
3477 display.prevInput = selected ? "" : "\u200b";
3478 display.input.selectionStart = 1; display.input.selectionEnd = extval.le ngth;
3479 // Re-set this, in case some other handler touched the
3480 // selection in the meantime.
3481 display.selForContextMenu = cm.doc.sel;
3482 }
3483 }
3484 function rehide() {
3485 display.contextMenuPending = false;
3486 display.inputDiv.style.position = "relative";
3487 display.input.style.cssText = oldCSS;
3488 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller .scrollTop = scrollPos);
3489 slowPoll(cm);
3490
3491 // Try to detect the user choosing select-all
3492 if (display.input.selectionStart != null) {
3493 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
3494 var i = 0, poll = function() {
3495 if (display.selForContextMenu == cm.doc.sel && display.input.selection Start == 0)
3496 operation(cm, commands.selectAll)(cm);
3497 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
3498 else resetInput(cm);
3499 };
3500 display.detectingSelectAll = setTimeout(poll, 200);
3501 }
3502 }
3503
3504 if (ie && ie_version >= 9) prepareSelectAllHack();
3505 if (captureRightClick) {
3506 e_stop(e);
3507 var mouseup = function() {
3508 off(window, "mouseup", mouseup);
3509 setTimeout(rehide, 20);
3510 };
3511 on(window, "mouseup", mouseup);
3512 } else {
3513 setTimeout(rehide, 50);
3514 }
3515 } 4293 }
3516 4294
3517 function contextMenuInGutter(cm, e) { 4295 function contextMenuInGutter(cm, e) {
3518 if (!hasHandler(cm, "gutterContextMenu")) return false; 4296 if (!hasHandler(cm, "gutterContextMenu")) return false;
3519 return gutterEvent(cm, e, "gutterContextMenu", false, signal); 4297 return gutterEvent(cm, e, "gutterContextMenu", false);
3520 } 4298 }
3521 4299
3522 // UPDATING 4300 // UPDATING
3523 4301
3524 // Compute the position of the end of a change (its 'to' property 4302 // Compute the position of the end of a change (its 'to' property
3525 // refers to the pre-change end). 4303 // refers to the pre-change end).
3526 var changeEnd = CodeMirror.changeEnd = function(change) { 4304 var changeEnd = CodeMirror.changeEnd = function(change) {
3527 if (!change.text) return change.to; 4305 if (!change.text) return change.to;
3528 return Pos(change.from.line + change.text.length - 1, 4306 return Pos(change.from.line + change.text.length - 1,
3529 lst(change.text).length + (change.text.length == 1 ? change.from. ch : 0)); 4307 lst(change.text).length + (change.text.length == 1 ? change.from. ch : 0));
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
3637 if (!sharedHist && indexOf(rebased, doc.history) == -1) { 4415 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
3638 rebaseHist(doc.history, change); 4416 rebaseHist(doc.history, change);
3639 rebased.push(doc.history); 4417 rebased.push(doc.history);
3640 } 4418 }
3641 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change) ); 4419 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change) );
3642 }); 4420 });
3643 } 4421 }
3644 4422
3645 // Revert a change stored in a document's history. 4423 // Revert a change stored in a document's history.
3646 function makeChangeFromHistory(doc, type, allowSelectionOnly) { 4424 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
3647 if (doc.cm && doc.cm.state.suppressEdits) return; 4425 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) return;
3648 4426
3649 var hist = doc.history, event, selAfter = doc.sel; 4427 var hist = doc.history, event, selAfter = doc.sel;
3650 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; 4428 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
3651 4429
3652 // Verify that there is a useable event (so that ctrl-z won't 4430 // Verify that there is a useable event (so that ctrl-z won't
3653 // needlessly clear selection events) 4431 // needlessly clear selection events)
3654 for (var i = 0; i < source.length; i++) { 4432 for (var i = 0; i < source.length; i++) {
3655 event = source[i]; 4433 event = source[i];
3656 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.r anges) 4434 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.r anges)
3657 break; 4435 break;
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
3813 }; 4591 };
3814 if (changeHandler) signalLater(cm, "change", cm, obj); 4592 if (changeHandler) signalLater(cm, "change", cm, obj);
3815 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).pu sh(obj); 4593 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).pu sh(obj);
3816 } 4594 }
3817 cm.display.selForContextMenu = null; 4595 cm.display.selForContextMenu = null;
3818 } 4596 }
3819 4597
3820 function replaceRange(doc, code, from, to, origin) { 4598 function replaceRange(doc, code, from, to, origin) {
3821 if (!to) to = from; 4599 if (!to) to = from;
3822 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } 4600 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
3823 if (typeof code == "string") code = splitLines(code); 4601 if (typeof code == "string") code = doc.splitLines(code);
3824 makeChange(doc, {from: from, to: to, text: code, origin: origin}); 4602 makeChange(doc, {from: from, to: to, text: code, origin: origin});
3825 } 4603 }
3826 4604
3827 // SCROLLING THINGS INTO VIEW 4605 // SCROLLING THINGS INTO VIEW
3828 4606
3829 // If an editor sits on the top or bottom of the window, partially 4607 // If an editor sits on the top or bottom of the window, partially
3830 // scrolled out of view, this ensures that the cursor is visible. 4608 // scrolled out of view, this ensures that the cursor is visible.
3831 function maybeScrollWindow(cm, coords) { 4609 function maybeScrollWindow(cm, coords) {
3832 if (signalDOMEvent(cm, "scrollCursorIntoView")) return; 4610 if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
3833 4611
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
3992 } 4770 }
3993 indentation = Math.max(0, indentation); 4771 indentation = Math.max(0, indentation);
3994 4772
3995 var indentString = "", pos = 0; 4773 var indentString = "", pos = 0;
3996 if (cm.options.indentWithTabs) 4774 if (cm.options.indentWithTabs)
3997 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i ndentString += "\t";} 4775 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i ndentString += "\t";}
3998 if (pos < indentation) indentString += spaceStr(indentation - pos); 4776 if (pos < indentation) indentString += spaceStr(indentation - pos);
3999 4777
4000 if (indentString != curSpaceString) { 4778 if (indentString != curSpaceString) {
4001 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); 4779 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
4780 line.stateAfter = null;
4781 return true;
4002 } else { 4782 } else {
4003 // Ensure that, if the cursor was in the whitespace at the start 4783 // Ensure that, if the cursor was in the whitespace at the start
4004 // of the line, it is moved to the end of that space. 4784 // of the line, it is moved to the end of that space.
4005 for (var i = 0; i < doc.sel.ranges.length; i++) { 4785 for (var i = 0; i < doc.sel.ranges.length; i++) {
4006 var range = doc.sel.ranges[i]; 4786 var range = doc.sel.ranges[i];
4007 if (range.head.line == n && range.head.ch < curSpaceString.length) { 4787 if (range.head.line == n && range.head.ch < curSpaceString.length) {
4008 var pos = Pos(n, curSpaceString.length); 4788 var pos = Pos(n, curSpaceString.length);
4009 replaceOneSelection(doc, i, new Range(pos, pos)); 4789 replaceOneSelection(doc, i, new Range(pos, pos));
4010 break; 4790 break;
4011 } 4791 }
4012 } 4792 }
4013 } 4793 }
4014 line.stateAfter = null;
4015 } 4794 }
4016 4795
4017 // Utility for applying a change to a line by handle or number, 4796 // Utility for applying a change to a line by handle or number,
4018 // returning the number and optionally registering the line as 4797 // returning the number and optionally registering the line as
4019 // changed. 4798 // changed.
4020 function changeLine(doc, handle, changeType, op) { 4799 function changeLine(doc, handle, changeType, op) {
4021 var no = handle, line = handle; 4800 var no = handle, line = handle;
4022 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); 4801 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
4023 else no = lineNo(handle); 4802 else no = lineNo(handle);
4024 if (no == null) return null; 4803 if (no == null) return null;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
4056 // cross line boundaries), "word" (across next word), or "group" (to 4835 // cross line boundaries), "word" (across next word), or "group" (to
4057 // the start of next group of word or non-word-non-whitespace 4836 // the start of next group of word or non-word-non-whitespace
4058 // chars). The visually param controls whether, in right-to-left 4837 // chars). The visually param controls whether, in right-to-left
4059 // text, direction 1 means to move towards the next index in the 4838 // text, direction 1 means to move towards the next index in the
4060 // string, or towards the character to the right of the current 4839 // string, or towards the character to the right of the current
4061 // position. The resulting position will have a hitSide=true 4840 // position. The resulting position will have a hitSide=true
4062 // property if it reached the end of the document. 4841 // property if it reached the end of the document.
4063 function findPosH(doc, pos, dir, unit, visually) { 4842 function findPosH(doc, pos, dir, unit, visually) {
4064 var line = pos.line, ch = pos.ch, origDir = dir; 4843 var line = pos.line, ch = pos.ch, origDir = dir;
4065 var lineObj = getLine(doc, line); 4844 var lineObj = getLine(doc, line);
4066 var possible = true;
4067 function findNextLine() { 4845 function findNextLine() {
4068 var l = line + dir; 4846 var l = line + dir;
4069 if (l < doc.first || l >= doc.first + doc.size) return (possible = false); 4847 if (l < doc.first || l >= doc.first + doc.size) return false
4070 line = l; 4848 line = l;
4071 return lineObj = getLine(doc, l); 4849 return lineObj = getLine(doc, l);
4072 } 4850 }
4073 function moveOnce(boundToLine) { 4851 function moveOnce(boundToLine) {
4074 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, tru e); 4852 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, tru e);
4075 if (next == null) { 4853 if (next == null) {
4076 if (!boundToLine && findNextLine()) { 4854 if (!boundToLine && findNextLine()) {
4077 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); 4855 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
4078 else ch = dir < 0 ? lineObj.text.length : 0; 4856 else ch = dir < 0 ? lineObj.text.length : 0;
4079 } else return (possible = false); 4857 } else return false
4080 } else ch = next; 4858 } else ch = next;
4081 return true; 4859 return true;
4082 } 4860 }
4083 4861
4084 if (unit == "char") moveOnce(); 4862 if (unit == "char") {
4085 else if (unit == "column") moveOnce(true); 4863 moveOnce()
4086 else if (unit == "word" || unit == "group") { 4864 } else if (unit == "column") {
4865 moveOnce(true)
4866 } else if (unit == "word" || unit == "group") {
4087 var sawType = null, group = unit == "group"; 4867 var sawType = null, group = unit == "group";
4088 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); 4868 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
4089 for (var first = true;; first = false) { 4869 for (var first = true;; first = false) {
4090 if (dir < 0 && !moveOnce(!first)) break; 4870 if (dir < 0 && !moveOnce(!first)) break;
4091 var cur = lineObj.text.charAt(ch) || "\n"; 4871 var cur = lineObj.text.charAt(ch) || "\n";
4092 var type = isWordChar(cur, helper) ? "w" 4872 var type = isWordChar(cur, helper) ? "w"
4093 : group && cur == "\n" ? "n" 4873 : group && cur == "\n" ? "n"
4094 : !group || /\s/.test(cur) ? null 4874 : !group || /\s/.test(cur) ? null
4095 : "p"; 4875 : "p";
4096 if (group && !first && !type) type = "s"; 4876 if (group && !first && !type) type = "s";
4097 if (sawType && sawType != type) { 4877 if (sawType && sawType != type) {
4098 if (dir < 0) {dir = 1; moveOnce();} 4878 if (dir < 0) {dir = 1; moveOnce();}
4099 break; 4879 break;
4100 } 4880 }
4101 4881
4102 if (type) sawType = type; 4882 if (type) sawType = type;
4103 if (dir > 0 && !moveOnce(!first)) break; 4883 if (dir > 0 && !moveOnce(!first)) break;
4104 } 4884 }
4105 } 4885 }
4106 var result = skipAtomic(doc, Pos(line, ch), origDir, true); 4886 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true);
4107 if (!possible) result.hitSide = true; 4887 if (!cmp(pos, result)) result.hitSide = true;
4108 return result; 4888 return result;
4109 } 4889 }
4110 4890
4111 // For relative vertical movement. Dir may be -1 or 1. Unit can be 4891 // For relative vertical movement. Dir may be -1 or 1. Unit can be
4112 // "page" or "line". The resulting position will have a hitSide=true 4892 // "page" or "line". The resulting position will have a hitSide=true
4113 // property if it reached the end of the document. 4893 // property if it reached the end of the document.
4114 function findPosV(cm, pos, dir, unit) { 4894 function findPosV(cm, pos, dir, unit) {
4115 var doc = cm.doc, x = pos.left, y; 4895 var doc = cm.doc, x = pos.left, y;
4116 if (unit == "page") { 4896 if (unit == "page") {
4117 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeigh t || document.documentElement.clientHeight); 4897 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeigh t || document.documentElement.clientHeight);
(...skipping 15 matching lines...) Expand all
4133 // The publicly visible API. Note that methodOp(f) means 4913 // The publicly visible API. Note that methodOp(f) means
4134 // 'wrap f in an operation, performed on its `this` parameter'. 4914 // 'wrap f in an operation, performed on its `this` parameter'.
4135 4915
4136 // This is not the complete set of editor methods. Most of the 4916 // This is not the complete set of editor methods. Most of the
4137 // methods defined on the Doc type are also injected into 4917 // methods defined on the Doc type are also injected into
4138 // CodeMirror.prototype, for backwards compatibility and 4918 // CodeMirror.prototype, for backwards compatibility and
4139 // convenience. 4919 // convenience.
4140 4920
4141 CodeMirror.prototype = { 4921 CodeMirror.prototype = {
4142 constructor: CodeMirror, 4922 constructor: CodeMirror,
4143 focus: function(){window.focus(); focusInput(this); fastPoll(this);}, 4923 focus: function(){window.focus(); this.display.input.focus();},
4144 4924
4145 setOption: function(option, value) { 4925 setOption: function(option, value) {
4146 var options = this.options, old = options[option]; 4926 var options = this.options, old = options[option];
4147 if (options[option] == value && option != "mode") return; 4927 if (options[option] == value && option != "mode") return;
4148 options[option] = value; 4928 options[option] = value;
4149 if (optionHandlers.hasOwnProperty(option)) 4929 if (optionHandlers.hasOwnProperty(option))
4150 operation(this, optionHandlers[option])(this, value, old); 4930 operation(this, optionHandlers[option])(this, value, old);
4151 }, 4931 },
4152 4932
4153 getOption: function(option) {return this.options[option];}, 4933 getOption: function(option) {return this.options[option];},
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
4244 if (!mode.innerMode) return mode; 5024 if (!mode.innerMode) return mode;
4245 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; 5025 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
4246 }, 5026 },
4247 5027
4248 getHelper: function(pos, type) { 5028 getHelper: function(pos, type) {
4249 return this.getHelpers(pos, type)[0]; 5029 return this.getHelpers(pos, type)[0];
4250 }, 5030 },
4251 5031
4252 getHelpers: function(pos, type) { 5032 getHelpers: function(pos, type) {
4253 var found = []; 5033 var found = [];
4254 if (!helpers.hasOwnProperty(type)) return helpers; 5034 if (!helpers.hasOwnProperty(type)) return found;
4255 var help = helpers[type], mode = this.getModeAt(pos); 5035 var help = helpers[type], mode = this.getModeAt(pos);
4256 if (typeof mode[type] == "string") { 5036 if (typeof mode[type] == "string") {
4257 if (help[mode[type]]) found.push(help[mode[type]]); 5037 if (help[mode[type]]) found.push(help[mode[type]]);
4258 } else if (mode[type]) { 5038 } else if (mode[type]) {
4259 for (var i = 0; i < mode[type].length; i++) { 5039 for (var i = 0; i < mode[type].length; i++) {
4260 var val = help[mode[type][i]]; 5040 var val = help[mode[type][i]];
4261 if (val) found.push(val); 5041 if (val) found.push(val);
4262 } 5042 }
4263 } else if (mode.helperType && help[mode.helperType]) { 5043 } else if (mode.helperType && help[mode.helperType]) {
4264 found.push(help[mode.helperType]); 5044 found.push(help[mode.helperType]);
(...skipping 29 matching lines...) Expand all
4294 coordsChar: function(coords, mode) { 5074 coordsChar: function(coords, mode) {
4295 coords = fromCoordSystem(this, coords, mode || "page"); 5075 coords = fromCoordSystem(this, coords, mode || "page");
4296 return coordsChar(this, coords.left, coords.top); 5076 return coordsChar(this, coords.left, coords.top);
4297 }, 5077 },
4298 5078
4299 lineAtHeight: function(height, mode) { 5079 lineAtHeight: function(height, mode) {
4300 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top ; 5080 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top ;
4301 return lineAtHeight(this.doc, height + this.display.viewOffset); 5081 return lineAtHeight(this.doc, height + this.display.viewOffset);
4302 }, 5082 },
4303 heightAtLine: function(line, mode) { 5083 heightAtLine: function(line, mode) {
4304 var end = false, last = this.doc.first + this.doc.size - 1; 5084 var end = false, lineObj;
4305 if (line < this.doc.first) line = this.doc.first; 5085 if (typeof line == "number") {
4306 else if (line > last) { line = last; end = true; } 5086 var last = this.doc.first + this.doc.size - 1;
4307 var lineObj = getLine(this.doc, line); 5087 if (line < this.doc.first) line = this.doc.first;
5088 else if (line > last) { line = last; end = true; }
5089 lineObj = getLine(this.doc, line);
5090 } else {
5091 lineObj = line;
5092 }
4308 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").t op + 5093 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").t op +
4309 (end ? this.doc.height - heightAtLine(lineObj) : 0); 5094 (end ? this.doc.height - heightAtLine(lineObj) : 0);
4310 }, 5095 },
4311 5096
4312 defaultTextHeight: function() { return textHeight(this.display); }, 5097 defaultTextHeight: function() { return textHeight(this.display); },
4313 defaultCharWidth: function() { return charWidth(this.display); }, 5098 defaultCharWidth: function() { return charWidth(this.display); },
4314 5099
4315 setGutterMarker: methodOp(function(line, gutterID, value) { 5100 setGutterMarker: methodOp(function(line, gutterID, value) {
4316 return changeLine(this.doc, line, "gutter", function(line) { 5101 return changeLine(this.doc, line, "gutter", function(line) {
4317 var markers = line.gutterMarkers || (line.gutterMarkers = {}); 5102 var markers = line.gutterMarkers || (line.gutterMarkers = {});
4318 markers[gutterID] = value; 5103 markers[gutterID] = value;
4319 if (!value && isEmpty(markers)) line.gutterMarkers = null; 5104 if (!value && isEmpty(markers)) line.gutterMarkers = null;
4320 return true; 5105 return true;
4321 }); 5106 });
4322 }), 5107 }),
4323 5108
4324 clearGutter: methodOp(function(gutterID) { 5109 clearGutter: methodOp(function(gutterID) {
4325 var cm = this, doc = cm.doc, i = doc.first; 5110 var cm = this, doc = cm.doc, i = doc.first;
4326 doc.iter(function(line) { 5111 doc.iter(function(line) {
4327 if (line.gutterMarkers && line.gutterMarkers[gutterID]) { 5112 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
4328 line.gutterMarkers[gutterID] = null; 5113 line.gutterMarkers[gutterID] = null;
4329 regLineChange(cm, i, "gutter"); 5114 regLineChange(cm, i, "gutter");
4330 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; 5115 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
4331 } 5116 }
4332 ++i; 5117 ++i;
4333 }); 5118 });
4334 }), 5119 }),
4335 5120
4336 addLineWidget: methodOp(function(handle, node, options) {
4337 return addLineWidget(this, handle, node, options);
4338 }),
4339
4340 removeLineWidget: function(widget) { widget.clear(); },
4341
4342 lineInfo: function(line) { 5121 lineInfo: function(line) {
4343 if (typeof line == "number") { 5122 if (typeof line == "number") {
4344 if (!isLine(this.doc, line)) return null; 5123 if (!isLine(this.doc, line)) return null;
4345 var n = line; 5124 var n = line;
4346 line = getLine(this.doc, line); 5125 line = getLine(this.doc, line);
4347 if (!line) return null; 5126 if (!line) return null;
4348 } else { 5127 } else {
4349 var n = lineNo(line); 5128 var n = lineNo(line);
4350 if (n == null) return null; 5129 if (n == null) return null;
4351 } 5130 }
4352 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutter Markers, 5131 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutter Markers,
4353 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line. wrapClass, 5132 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line. wrapClass,
4354 widgets: line.widgets}; 5133 widgets: line.widgets};
4355 }, 5134 },
4356 5135
4357 getViewport: function() { return {from: this.display.viewFrom, to: this.disp lay.viewTo};}, 5136 getViewport: function() { return {from: this.display.viewFrom, to: this.disp lay.viewTo};},
4358 5137
4359 addWidget: function(pos, node, scroll, vert, horiz) { 5138 addWidget: function(pos, node, scroll, vert, horiz) {
4360 var display = this.display; 5139 var display = this.display;
4361 pos = cursorCoords(this, clipPos(this.doc, pos)); 5140 pos = cursorCoords(this, clipPos(this.doc, pos));
4362 var top = pos.bottom, left = pos.left; 5141 var top = pos.bottom, left = pos.left;
4363 node.style.position = "absolute"; 5142 node.style.position = "absolute";
4364 node.setAttribute("cm-ignore-events", "true"); 5143 node.setAttribute("cm-ignore-events", "true");
5144 this.display.input.setUneditable(node);
4365 display.sizer.appendChild(node); 5145 display.sizer.appendChild(node);
4366 if (vert == "over") { 5146 if (vert == "over") {
4367 top = pos.top; 5147 top = pos.top;
4368 } else if (vert == "above" || vert == "near") { 5148 } else if (vert == "above" || vert == "near") {
4369 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), 5149 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
4370 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWid th); 5150 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWid th);
4371 // Default to positioning above (if specified and possible); otherwise d efault to positioning below 5151 // Default to positioning above (if specified and possible); otherwise d efault to positioning below
4372 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos. top > node.offsetHeight) 5152 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos. top > node.offsetHeight)
4373 top = pos.top - node.offsetHeight; 5153 top = pos.top - node.offsetHeight;
4374 else if (pos.bottom + node.offsetHeight <= vspace) 5154 else if (pos.bottom + node.offsetHeight <= vspace)
(...skipping 14 matching lines...) Expand all
4389 if (scroll) 5169 if (scroll)
4390 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offs etHeight); 5170 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offs etHeight);
4391 }, 5171 },
4392 5172
4393 triggerOnKeyDown: methodOp(onKeyDown), 5173 triggerOnKeyDown: methodOp(onKeyDown),
4394 triggerOnKeyPress: methodOp(onKeyPress), 5174 triggerOnKeyPress: methodOp(onKeyPress),
4395 triggerOnKeyUp: onKeyUp, 5175 triggerOnKeyUp: onKeyUp,
4396 5176
4397 execCommand: function(cmd) { 5177 execCommand: function(cmd) {
4398 if (commands.hasOwnProperty(cmd)) 5178 if (commands.hasOwnProperty(cmd))
4399 return commands[cmd](this); 5179 return commands[cmd].call(null, this);
4400 }, 5180 },
4401 5181
5182 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
5183
4402 findPosH: function(from, amount, unit, visually) { 5184 findPosH: function(from, amount, unit, visually) {
4403 var dir = 1; 5185 var dir = 1;
4404 if (amount < 0) { dir = -1; amount = -amount; } 5186 if (amount < 0) { dir = -1; amount = -amount; }
4405 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { 5187 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
4406 cur = findPosH(this.doc, cur, dir, unit, visually); 5188 cur = findPosH(this.doc, cur, dir, unit, visually);
4407 if (cur.hitSide) break; 5189 if (cur.hitSide) break;
4408 } 5190 }
4409 return cur; 5191 return cur;
4410 }, 5192 },
4411 5193
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
4481 5263
4482 toggleOverwrite: function(value) { 5264 toggleOverwrite: function(value) {
4483 if (value != null && value == this.state.overwrite) return; 5265 if (value != null && value == this.state.overwrite) return;
4484 if (this.state.overwrite = !this.state.overwrite) 5266 if (this.state.overwrite = !this.state.overwrite)
4485 addClass(this.display.cursorDiv, "CodeMirror-overwrite"); 5267 addClass(this.display.cursorDiv, "CodeMirror-overwrite");
4486 else 5268 else
4487 rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); 5269 rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
4488 5270
4489 signal(this, "overwriteToggle", this, this.state.overwrite); 5271 signal(this, "overwriteToggle", this, this.state.overwrite);
4490 }, 5272 },
4491 hasFocus: function() { return activeElt() == this.display.input; }, 5273 hasFocus: function() { return this.display.input.getField() == activeElt(); },
5274 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdi t); },
4492 5275
4493 scrollTo: methodOp(function(x, y) { 5276 scrollTo: methodOp(function(x, y) {
4494 if (x != null || y != null) resolveScrollToPos(this); 5277 if (x != null || y != null) resolveScrollToPos(this);
4495 if (x != null) this.curOp.scrollLeft = x; 5278 if (x != null) this.curOp.scrollLeft = x;
4496 if (y != null) this.curOp.scrollTop = y; 5279 if (y != null) this.curOp.scrollTop = y;
4497 }), 5280 }),
4498 getScrollInfo: function() { 5281 getScrollInfo: function() {
4499 var scroller = this.display.scroller; 5282 var scroller = this.display.scroller;
4500 return {left: scroller.scrollLeft, top: scroller.scrollTop, 5283 return {left: scroller.scrollLeft, top: scroller.scrollTop,
4501 height: scroller.scrollHeight - scrollGap(this) - this.display.bar Height, 5284 height: scroller.scrollHeight - scrollGap(this) - this.display.bar Height,
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
4557 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) 5340 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
4558 estimateLineHeights(this); 5341 estimateLineHeights(this);
4559 signal(this, "refresh", this); 5342 signal(this, "refresh", this);
4560 }), 5343 }),
4561 5344
4562 swapDoc: methodOp(function(doc) { 5345 swapDoc: methodOp(function(doc) {
4563 var old = this.doc; 5346 var old = this.doc;
4564 old.cm = null; 5347 old.cm = null;
4565 attachDoc(this, doc); 5348 attachDoc(this, doc);
4566 clearCaches(this); 5349 clearCaches(this);
4567 resetInput(this); 5350 this.display.input.reset();
4568 this.scrollTo(doc.scrollLeft, doc.scrollTop); 5351 this.scrollTo(doc.scrollLeft, doc.scrollTop);
4569 this.curOp.forceScroll = true; 5352 this.curOp.forceScroll = true;
4570 signalLater(this, "swapDoc", this, old); 5353 signalLater(this, "swapDoc", this, old);
4571 return old; 5354 return old;
4572 }), 5355 }),
4573 5356
4574 getInputField: function(){return this.display.input;}, 5357 getInputField: function(){return this.display.input.getField();},
4575 getWrapperElement: function(){return this.display.wrapper;}, 5358 getWrapperElement: function(){return this.display.wrapper;},
4576 getScrollerElement: function(){return this.display.scroller;}, 5359 getScrollerElement: function(){return this.display.scroller;},
4577 getGutterElement: function(){return this.display.gutters;} 5360 getGutterElement: function(){return this.display.gutters;}
4578 }; 5361 };
4579 eventMixin(CodeMirror); 5362 eventMixin(CodeMirror);
4580 5363
4581 // OPTION DEFAULTS 5364 // OPTION DEFAULTS
4582 5365
4583 // The default configuration options. 5366 // The default configuration options.
4584 var defaults = CodeMirror.defaults = {}; 5367 var defaults = CodeMirror.defaults = {};
(...skipping 20 matching lines...) Expand all
4605 }, true); 5388 }, true);
4606 5389
4607 option("indentUnit", 2, loadMode, true); 5390 option("indentUnit", 2, loadMode, true);
4608 option("indentWithTabs", false); 5391 option("indentWithTabs", false);
4609 option("smartIndent", true); 5392 option("smartIndent", true);
4610 option("tabSize", 4, function(cm) { 5393 option("tabSize", 4, function(cm) {
4611 resetModeState(cm); 5394 resetModeState(cm);
4612 clearCaches(cm); 5395 clearCaches(cm);
4613 regChange(cm); 5396 regChange(cm);
4614 }, true); 5397 }, true);
4615 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff] /g, function(cm, val) { 5398 option("lineSeparator", null, function(cm, val) {
4616 cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\ t"), "g"); 5399 cm.doc.lineSep = val;
4617 cm.refresh(); 5400 if (!val) return;
4618 }, true); 5401 var newBreaks = [], lineNo = cm.doc.first;
5402 cm.doc.iter(function(line) {
5403 for (var pos = 0;;) {
5404 var found = line.text.indexOf(val, pos);
5405 if (found == -1) break;
5406 pos = found + val.length;
5407 newBreaks.push(Pos(lineNo, found));
5408 }
5409 lineNo++;
5410 });
5411 for (var i = newBreaks.length - 1; i >= 0; i--)
5412 replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i ].ch + val.length))
5413 });
5414 option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\uf eff]/g, function(cm, val, old) {
5415 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t" ), "g");
5416 if (old != CodeMirror.Init) cm.refresh();
5417 });
4619 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) { cm.refresh();}, true); 5418 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) { cm.refresh();}, true);
4620 option("electricChars", true); 5419 option("electricChars", true);
5420 option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
5421 throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
5422 }, true);
4621 option("rtlMoveVisually", !windows); 5423 option("rtlMoveVisually", !windows);
4622 option("wholeLineUpdateBefore", true); 5424 option("wholeLineUpdateBefore", true);
4623 5425
4624 option("theme", "default", function(cm) { 5426 option("theme", "default", function(cm) {
4625 themeChanged(cm); 5427 themeChanged(cm);
4626 guttersChanged(cm); 5428 guttersChanged(cm);
4627 }, true); 5429 }, true);
4628 option("keyMap", "default", function(cm, val, old) { 5430 option("keyMap", "default", function(cm, val, old) {
4629 var next = getKeyMap(val); 5431 var next = getKeyMap(val);
4630 var prev = old != CodeMirror.Init && getKeyMap(old); 5432 var prev = old != CodeMirror.Init && getKeyMap(old);
(...skipping 20 matching lines...) Expand all
4651 }, true); 5453 }, true);
4652 option("lineNumbers", false, function(cm) { 5454 option("lineNumbers", false, function(cm) {
4653 setGuttersForLineNumbers(cm.options); 5455 setGuttersForLineNumbers(cm.options);
4654 guttersChanged(cm); 5456 guttersChanged(cm);
4655 }, true); 5457 }, true);
4656 option("firstLineNumber", 1, guttersChanged, true); 5458 option("firstLineNumber", 1, guttersChanged, true);
4657 option("lineNumberFormatter", function(integer) {return integer;}, guttersChan ged, true); 5459 option("lineNumberFormatter", function(integer) {return integer;}, guttersChan ged, true);
4658 option("showCursorWhenSelecting", false, updateSelection, true); 5460 option("showCursorWhenSelecting", false, updateSelection, true);
4659 5461
4660 option("resetSelectionOnContextMenu", true); 5462 option("resetSelectionOnContextMenu", true);
5463 option("lineWiseCopyCut", true);
4661 5464
4662 option("readOnly", false, function(cm, val) { 5465 option("readOnly", false, function(cm, val) {
4663 if (val == "nocursor") { 5466 if (val == "nocursor") {
4664 onBlur(cm); 5467 onBlur(cm);
4665 cm.display.input.blur(); 5468 cm.display.input.blur();
4666 cm.display.disabled = true; 5469 cm.display.disabled = true;
4667 } else { 5470 } else {
4668 cm.display.disabled = false; 5471 cm.display.disabled = false;
4669 if (!val) resetInput(cm);
4670 } 5472 }
5473 cm.display.input.readOnlyChanged(val)
4671 }); 5474 });
4672 option("disableInput", false, function(cm, val) {if (!val) resetInput(cm);}, t rue); 5475 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.re set();}, true);
4673 option("dragDrop", true); 5476 option("dragDrop", true, dragDropChanged);
5477 option("allowDropFileTypes", null);
4674 5478
4675 option("cursorBlinkRate", 530); 5479 option("cursorBlinkRate", 530);
4676 option("cursorScrollMargin", 0); 5480 option("cursorScrollMargin", 0);
4677 option("cursorHeight", 1, updateSelection, true); 5481 option("cursorHeight", 1, updateSelection, true);
4678 option("singleCursorHeightPerLine", true, updateSelection, true); 5482 option("singleCursorHeightPerLine", true, updateSelection, true);
4679 option("workTime", 100); 5483 option("workTime", 100);
4680 option("workDelay", 100); 5484 option("workDelay", 100);
4681 option("flattenSpans", true, resetModeState, true); 5485 option("flattenSpans", true, resetModeState, true);
4682 option("addModeClass", false, resetModeState, true); 5486 option("addModeClass", false, resetModeState, true);
4683 option("pollInterval", 100); 5487 option("pollInterval", 100);
4684 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;}); 5488 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
4685 option("historyEventDelay", 1250); 5489 option("historyEventDelay", 1250);
4686 option("viewportMargin", 10, function(cm){cm.refresh();}, true); 5490 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
4687 option("maxHighlightLength", 10000, resetModeState, true); 5491 option("maxHighlightLength", 10000, resetModeState, true);
4688 option("moveInputWithCursor", true, function(cm, val) { 5492 option("moveInputWithCursor", true, function(cm, val) {
4689 if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0 ; 5493 if (!val) cm.display.input.resetPosition();
4690 }); 5494 });
4691 5495
4692 option("tabindex", null, function(cm, val) { 5496 option("tabindex", null, function(cm, val) {
4693 cm.display.input.tabIndex = val || ""; 5497 cm.display.input.getField().tabIndex = val || "";
4694 }); 5498 });
4695 option("autofocus", null); 5499 option("autofocus", null);
4696 5500
4697 // MODE DEFINITION AND QUERYING 5501 // MODE DEFINITION AND QUERYING
4698 5502
4699 // Known modes, by name and by MIME 5503 // Known modes, by name and by MIME
4700 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; 5504 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
4701 5505
4702 // Extra arguments are stored as the mode's dependencies, which is 5506 // Extra arguments are stored as the mode's dependencies, which is
4703 // used by (legacy) mechanisms like loadmode.js to automatically 5507 // used by (legacy) mechanisms like loadmode.js to automatically
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after
4929 delGroupAfter: function(cm) {cm.deleteH(1, "group");}, 5733 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
4930 indentAuto: function(cm) {cm.indentSelection("smart");}, 5734 indentAuto: function(cm) {cm.indentSelection("smart");},
4931 indentMore: function(cm) {cm.indentSelection("add");}, 5735 indentMore: function(cm) {cm.indentSelection("add");},
4932 indentLess: function(cm) {cm.indentSelection("subtract");}, 5736 indentLess: function(cm) {cm.indentSelection("subtract");},
4933 insertTab: function(cm) {cm.replaceSelection("\t");}, 5737 insertTab: function(cm) {cm.replaceSelection("\t");},
4934 insertSoftTab: function(cm) { 5738 insertSoftTab: function(cm) {
4935 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSiz e; 5739 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSiz e;
4936 for (var i = 0; i < ranges.length; i++) { 5740 for (var i = 0; i < ranges.length; i++) {
4937 var pos = ranges[i].from(); 5741 var pos = ranges[i].from();
4938 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); 5742 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
4939 spaces.push(new Array(tabSize - col % tabSize + 1).join(" ")); 5743 spaces.push(spaceStr(tabSize - col % tabSize));
4940 } 5744 }
4941 cm.replaceSelections(spaces); 5745 cm.replaceSelections(spaces);
4942 }, 5746 },
4943 defaultTab: function(cm) { 5747 defaultTab: function(cm) {
4944 if (cm.somethingSelected()) cm.indentSelection("add"); 5748 if (cm.somethingSelected()) cm.indentSelection("add");
4945 else cm.execCommand("insertTab"); 5749 else cm.execCommand("insertTab");
4946 }, 5750 },
4947 transposeChars: function(cm) { 5751 transposeChars: function(cm) {
4948 runInOp(cm, function() { 5752 runInOp(cm, function() {
4949 var ranges = cm.listSelections(), newSel = []; 5753 var ranges = cm.listSelections(), newSel = [];
4950 for (var i = 0; i < ranges.length; i++) { 5754 for (var i = 0; i < ranges.length; i++) {
4951 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; 5755 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
4952 if (line) { 5756 if (line) {
4953 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); 5757 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
4954 if (cur.ch > 0) { 5758 if (cur.ch > 0) {
4955 cur = new Pos(cur.line, cur.ch + 1); 5759 cur = new Pos(cur.line, cur.ch + 1);
4956 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), 5760 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
4957 Pos(cur.line, cur.ch - 2), cur, "+transpose"); 5761 Pos(cur.line, cur.ch - 2), cur, "+transpose");
4958 } else if (cur.line > cm.doc.first) { 5762 } else if (cur.line > cm.doc.first) {
4959 var prev = getLine(cm.doc, cur.line - 1).text; 5763 var prev = getLine(cm.doc, cur.line - 1).text;
4960 if (prev) 5764 if (prev)
4961 cm.replaceRange(line.charAt(0) + "\n" + prev.charAt(prev.length - 1), 5765 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
5766 prev.charAt(prev.length - 1),
4962 Pos(cur.line - 1, prev.length - 1), Pos(cur.line , 1), "+transpose"); 5767 Pos(cur.line - 1, prev.length - 1), Pos(cur.line , 1), "+transpose");
4963 } 5768 }
4964 } 5769 }
4965 newSel.push(new Range(cur, cur)); 5770 newSel.push(new Range(cur, cur));
4966 } 5771 }
4967 cm.setSelections(newSel); 5772 cm.setSelections(newSel);
4968 }); 5773 });
4969 }, 5774 },
4970 newlineAndIndent: function(cm) { 5775 newlineAndIndent: function(cm) {
4971 runInOp(cm, function() { 5776 runInOp(cm, function() {
4972 var len = cm.listSelections().length; 5777 var len = cm.listSelections().length;
4973 for (var i = 0; i < len; i++) { 5778 for (var i = 0; i < len; i++) {
4974 var range = cm.listSelections()[i]; 5779 var range = cm.listSelections()[i];
4975 cm.replaceRange("\n", range.anchor, range.head, "+input"); 5780 cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+in put");
4976 cm.indentLine(range.from().line + 1, null, true); 5781 cm.indentLine(range.from().line + 1, null, true);
4977 ensureCursorVisible(cm);
4978 } 5782 }
5783 ensureCursorVisible(cm);
4979 }); 5784 });
4980 }, 5785 },
5786 openLine: function(cm) {cm.replaceSelection("\n", "start")},
4981 toggleOverwrite: function(cm) {cm.toggleOverwrite();} 5787 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
4982 }; 5788 };
4983 5789
4984 5790
4985 // STANDARD KEYMAPS 5791 // STANDARD KEYMAPS
4986 5792
4987 var keyMap = CodeMirror.keyMap = {}; 5793 var keyMap = CodeMirror.keyMap = {};
4988 5794
4989 keyMap.basic = { 5795 keyMap.basic = {
4990 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goL ineDown", 5796 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goL ineDown",
(...skipping 14 matching lines...) Expand all
5005 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", 5811 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
5006 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", 5812 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
5007 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSe lection", 5813 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSe lection",
5008 fallthrough: "basic" 5814 fallthrough: "basic"
5009 }; 5815 };
5010 // Very basic readline/emacs-style bindings, which are standard on Mac. 5816 // Very basic readline/emacs-style bindings, which are standard on Mac.
5011 keyMap.emacsy = { 5817 keyMap.emacsy = {
5012 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl -N": "goLineDown", 5818 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl -N": "goLineDown",
5013 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctr l-E": "goLineEnd", 5819 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctr l-E": "goLineEnd",
5014 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter" , "Ctrl-H": "delCharBefore", 5820 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter" , "Ctrl-H": "delCharBefore",
5015 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLi ne", "Ctrl-T": "transposeChars" 5821 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLi ne", "Ctrl-T": "transposeChars",
5822 "Ctrl-O": "openLine"
5016 }; 5823 };
5017 keyMap.macDefault = { 5824 keyMap.macDefault = {
5018 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", 5825 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
5019 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cm d-Down": "goDocEnd", "Alt-Left": "goGroupLeft", 5826 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cm d-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
5020 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineR ight", "Alt-Backspace": "delGroupBefore", 5827 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineR ight", "Alt-Backspace": "delGroupBefore",
5021 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S ": "save", "Cmd-F": "find", 5828 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S ": "save", "Cmd-F": "find",
5022 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shi ft-Cmd-Alt-F": "replaceAll", 5829 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shi ft-Cmd-Alt-F": "replaceAll",
5023 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLi neLeft", "Cmd-Delete": "delWrappedLineRight", 5830 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLi neLeft", "Cmd-Delete": "delWrappedLineRight",
5024 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocS tart", "Ctrl-Down": "goDocEnd", 5831 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocS tart", "Ctrl-Down": "goDocEnd",
5025 fallthrough: ["basic", "emacsy"] 5832 fallthrough: ["basic", "emacsy"]
(...skipping 29 matching lines...) Expand all
5055 var copy = {}; 5862 var copy = {};
5056 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) { 5863 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
5057 var value = keymap[keyname]; 5864 var value = keymap[keyname];
5058 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue; 5865 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
5059 if (value == "...") { delete keymap[keyname]; continue; } 5866 if (value == "...") { delete keymap[keyname]; continue; }
5060 5867
5061 var keys = map(keyname.split(" "), normalizeKeyName); 5868 var keys = map(keyname.split(" "), normalizeKeyName);
5062 for (var i = 0; i < keys.length; i++) { 5869 for (var i = 0; i < keys.length; i++) {
5063 var val, name; 5870 var val, name;
5064 if (i == keys.length - 1) { 5871 if (i == keys.length - 1) {
5065 name = keyname; 5872 name = keys.join(" ");
5066 val = value; 5873 val = value;
5067 } else { 5874 } else {
5068 name = keys.slice(0, i + 1).join(" "); 5875 name = keys.slice(0, i + 1).join(" ");
5069 val = "..."; 5876 val = "...";
5070 } 5877 }
5071 var prev = copy[name]; 5878 var prev = copy[name];
5072 if (!prev) copy[name] = val; 5879 if (!prev) copy[name] = val;
5073 else if (prev != val) throw new Error("Inconsistent bindings for " + nam e); 5880 else if (prev != val) throw new Error("Inconsistent bindings for " + nam e);
5074 } 5881 }
5075 delete keymap[keyname]; 5882 delete keymap[keyname];
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
5114 return name; 5921 return name;
5115 }; 5922 };
5116 5923
5117 function getKeyMap(val) { 5924 function getKeyMap(val) {
5118 return typeof val == "string" ? keyMap[val] : val; 5925 return typeof val == "string" ? keyMap[val] : val;
5119 } 5926 }
5120 5927
5121 // FROMTEXTAREA 5928 // FROMTEXTAREA
5122 5929
5123 CodeMirror.fromTextArea = function(textarea, options) { 5930 CodeMirror.fromTextArea = function(textarea, options) {
5124 if (!options) options = {}; 5931 options = options ? copyObj(options) : {};
5125 options.value = textarea.value; 5932 options.value = textarea.value;
5126 if (!options.tabindex && textarea.tabindex) 5933 if (!options.tabindex && textarea.tabIndex)
5127 options.tabindex = textarea.tabindex; 5934 options.tabindex = textarea.tabIndex;
5128 if (!options.placeholder && textarea.placeholder) 5935 if (!options.placeholder && textarea.placeholder)
5129 options.placeholder = textarea.placeholder; 5936 options.placeholder = textarea.placeholder;
5130 // Set autofocus to true if this textarea is focused, or if it has 5937 // Set autofocus to true if this textarea is focused, or if it has
5131 // autofocus and no other element is focused. 5938 // autofocus and no other element is focused.
5132 if (options.autofocus == null) { 5939 if (options.autofocus == null) {
5133 var hasFocus = activeElt(); 5940 var hasFocus = activeElt();
5134 options.autofocus = hasFocus == textarea || 5941 options.autofocus = hasFocus == textarea ||
5135 textarea.getAttribute("autofocus") != null && hasFocus == document.body; 5942 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
5136 } 5943 }
5137 5944
5138 function save() {textarea.value = cm.getValue();} 5945 function save() {textarea.value = cm.getValue();}
5139 if (textarea.form) { 5946 if (textarea.form) {
5140 on(textarea.form, "submit", save); 5947 on(textarea.form, "submit", save);
5141 // Deplorable hack to make the submit method do the right thing. 5948 // Deplorable hack to make the submit method do the right thing.
5142 if (!options.leaveSubmitMethodAlone) { 5949 if (!options.leaveSubmitMethodAlone) {
5143 var form = textarea.form, realSubmit = form.submit; 5950 var form = textarea.form, realSubmit = form.submit;
5144 try { 5951 try {
5145 var wrappedSubmit = form.submit = function() { 5952 var wrappedSubmit = form.submit = function() {
5146 save(); 5953 save();
5147 form.submit = realSubmit; 5954 form.submit = realSubmit;
5148 form.submit(); 5955 form.submit();
5149 form.submit = wrappedSubmit; 5956 form.submit = wrappedSubmit;
5150 }; 5957 };
5151 } catch(e) {} 5958 } catch(e) {}
5152 } 5959 }
5153 } 5960 }
5154 5961
5962 options.finishInit = function(cm) {
5963 cm.save = save;
5964 cm.getTextArea = function() { return textarea; };
5965 cm.toTextArea = function() {
5966 cm.toTextArea = isNaN; // Prevent this from being ran twice
5967 save();
5968 textarea.parentNode.removeChild(cm.getWrapperElement());
5969 textarea.style.display = "";
5970 if (textarea.form) {
5971 off(textarea.form, "submit", save);
5972 if (typeof textarea.form.submit == "function")
5973 textarea.form.submit = realSubmit;
5974 }
5975 };
5976 };
5977
5155 textarea.style.display = "none"; 5978 textarea.style.display = "none";
5156 var cm = CodeMirror(function(node) { 5979 var cm = CodeMirror(function(node) {
5157 textarea.parentNode.insertBefore(node, textarea.nextSibling); 5980 textarea.parentNode.insertBefore(node, textarea.nextSibling);
5158 }, options); 5981 }, options);
5159 cm.save = save;
5160 cm.getTextArea = function() { return textarea; };
5161 cm.toTextArea = function() {
5162 cm.toTextArea = isNaN; // Prevent this from being ran twice
5163 save();
5164 textarea.parentNode.removeChild(cm.getWrapperElement());
5165 textarea.style.display = "";
5166 if (textarea.form) {
5167 off(textarea.form, "submit", save);
5168 if (typeof textarea.form.submit == "function")
5169 textarea.form.submit = realSubmit;
5170 }
5171 };
5172 return cm; 5982 return cm;
5173 }; 5983 };
5174 5984
5175 // STRING STREAM 5985 // STRING STREAM
5176 5986
5177 // Fed to the mode parsers, provides helper functions to make 5987 // Fed to the mode parsers, provides helper functions to make
5178 // parsers more succinct. 5988 // parsers more succinct.
5179 5989
5180 var StringStream = CodeMirror.StringStream = function(string, tabSize) { 5990 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
5181 this.pos = this.start = 0; 5991 this.pos = this.start = 0;
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
5254 // Created with markText and setBookmark methods. A TextMarker is a 6064 // Created with markText and setBookmark methods. A TextMarker is a
5255 // handle that can be used to clear or find a marked position in the 6065 // handle that can be used to clear or find a marked position in the
5256 // document. Line objects hold arrays (markedSpans) containing 6066 // document. Line objects hold arrays (markedSpans) containing
5257 // {from, to, marker} object pointing to such marker objects, and 6067 // {from, to, marker} object pointing to such marker objects, and
5258 // indicating that such a marker is present on that line. Multiple 6068 // indicating that such a marker is present on that line. Multiple
5259 // lines may point to the same marker when it spans across lines. 6069 // lines may point to the same marker when it spans across lines.
5260 // The spans will have null for their from/to properties when the 6070 // The spans will have null for their from/to properties when the
5261 // marker continues beyond the start/end of the line. Markers have 6071 // marker continues beyond the start/end of the line. Markers have
5262 // links back to the lines they currently touch. 6072 // links back to the lines they currently touch.
5263 6073
6074 var nextMarkerId = 0;
6075
5264 var TextMarker = CodeMirror.TextMarker = function(doc, type) { 6076 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
5265 this.lines = []; 6077 this.lines = [];
5266 this.type = type; 6078 this.type = type;
5267 this.doc = doc; 6079 this.doc = doc;
6080 this.id = ++nextMarkerId;
5268 }; 6081 };
5269 eventMixin(TextMarker); 6082 eventMixin(TextMarker);
5270 6083
5271 // Clear the marker. 6084 // Clear the marker.
5272 TextMarker.prototype.clear = function() { 6085 TextMarker.prototype.clear = function() {
5273 if (this.explicitlyCleared) return; 6086 if (this.explicitlyCleared) return;
5274 var cm = this.doc.cm, withOp = cm && !cm.curOp; 6087 var cm = this.doc.cm, withOp = cm && !cm.curOp;
5275 if (withOp) startOperation(cm); 6088 if (withOp) startOperation(cm);
5276 if (hasHandler(this, "clear")) { 6089 if (hasHandler(this, "clear")) {
5277 var found = this.find(); 6090 var found = this.find();
(...skipping 483 matching lines...) Expand 10 before | Expand all | Expand 10 after
5761 function conflictingCollapsedRange(doc, lineNo, from, to, marker) { 6574 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
5762 var line = getLine(doc, lineNo); 6575 var line = getLine(doc, lineNo);
5763 var sps = sawCollapsedSpans && line.markedSpans; 6576 var sps = sawCollapsedSpans && line.markedSpans;
5764 if (sps) for (var i = 0; i < sps.length; ++i) { 6577 if (sps) for (var i = 0; i < sps.length; ++i) {
5765 var sp = sps[i]; 6578 var sp = sps[i];
5766 if (!sp.marker.collapsed) continue; 6579 if (!sp.marker.collapsed) continue;
5767 var found = sp.marker.find(0); 6580 var found = sp.marker.find(0);
5768 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(ma rker); 6581 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(ma rker);
5769 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker ); 6582 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker );
5770 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; 6583 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
5771 if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) || 6584 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cm p(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
5772 fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft & & marker.inclusiveRight))) 6585 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cm p(found.from, to) <= 0 : cmp(found.from, to) < 0))
5773 return true; 6586 return true;
5774 } 6587 }
5775 } 6588 }
5776 6589
5777 // A visual line is a line as drawn on the screen. Folding, for 6590 // A visual line is a line as drawn on the screen. Folding, for
5778 // example, can cause multiple logical lines to appear on the same 6591 // example, can cause multiple logical lines to appear on the same
5779 // visual line. This finds the start of the visual line that the 6592 // visual line. This finds the start of the visual line that the
5780 // given line is part of (usually that is the line itself). 6593 // given line is part of (usually that is the line itself).
5781 function visualLine(line) { 6594 function visualLine(line) {
5782 var merged; 6595 var merged;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
5841 (sp.to == null || sp.to != span.from) && 6654 (sp.to == null || sp.to != span.from) &&
5842 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && 6655 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
5843 lineIsHiddenInner(doc, line, sp)) return true; 6656 lineIsHiddenInner(doc, line, sp)) return true;
5844 } 6657 }
5845 } 6658 }
5846 6659
5847 // LINE WIDGETS 6660 // LINE WIDGETS
5848 6661
5849 // Line widgets are block elements displayed above or below a line. 6662 // Line widgets are block elements displayed above or below a line.
5850 6663
5851 var LineWidget = CodeMirror.LineWidget = function(cm, node, options) { 6664 var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
5852 if (options) for (var opt in options) if (options.hasOwnProperty(opt)) 6665 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
5853 this[opt] = options[opt]; 6666 this[opt] = options[opt];
5854 this.cm = cm; 6667 this.doc = doc;
5855 this.node = node; 6668 this.node = node;
5856 }; 6669 };
5857 eventMixin(LineWidget); 6670 eventMixin(LineWidget);
5858 6671
5859 function adjustScrollWhenAboveVisible(cm, line, diff) { 6672 function adjustScrollWhenAboveVisible(cm, line, diff) {
5860 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollT op)) 6673 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollT op))
5861 addToScrollPos(cm, null, diff); 6674 addToScrollPos(cm, null, diff);
5862 } 6675 }
5863 6676
5864 LineWidget.prototype.clear = function() { 6677 LineWidget.prototype.clear = function() {
5865 var cm = this.cm, ws = this.line.widgets, line = this.line, no = lineNo(line ); 6678 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo( line);
5866 if (no == null || !ws) return; 6679 if (no == null || !ws) return;
5867 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); 6680 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
5868 if (!ws.length) line.widgets = null; 6681 if (!ws.length) line.widgets = null;
5869 var height = widgetHeight(this); 6682 var height = widgetHeight(this);
5870 runInOp(cm, function() { 6683 updateLineHeight(line, Math.max(0, line.height - height));
6684 if (cm) runInOp(cm, function() {
5871 adjustScrollWhenAboveVisible(cm, line, -height); 6685 adjustScrollWhenAboveVisible(cm, line, -height);
5872 regLineChange(cm, no, "widget"); 6686 regLineChange(cm, no, "widget");
5873 updateLineHeight(line, Math.max(0, line.height - height));
5874 }); 6687 });
5875 }; 6688 };
5876 LineWidget.prototype.changed = function() { 6689 LineWidget.prototype.changed = function() {
5877 var oldH = this.height, cm = this.cm, line = this.line; 6690 var oldH = this.height, cm = this.doc.cm, line = this.line;
5878 this.height = null; 6691 this.height = null;
5879 var diff = widgetHeight(this) - oldH; 6692 var diff = widgetHeight(this) - oldH;
5880 if (!diff) return; 6693 if (!diff) return;
5881 runInOp(cm, function() { 6694 updateLineHeight(line, line.height + diff);
6695 if (cm) runInOp(cm, function() {
5882 cm.curOp.forceUpdate = true; 6696 cm.curOp.forceUpdate = true;
5883 adjustScrollWhenAboveVisible(cm, line, diff); 6697 adjustScrollWhenAboveVisible(cm, line, diff);
5884 updateLineHeight(line, line.height + diff);
5885 }); 6698 });
5886 }; 6699 };
5887 6700
5888 function widgetHeight(widget) { 6701 function widgetHeight(widget) {
5889 if (widget.height != null) return widget.height; 6702 if (widget.height != null) return widget.height;
6703 var cm = widget.doc.cm;
6704 if (!cm) return 0;
5890 if (!contains(document.body, widget.node)) { 6705 if (!contains(document.body, widget.node)) {
5891 var parentStyle = "position: relative;"; 6706 var parentStyle = "position: relative;";
5892 if (widget.coverGutter) 6707 if (widget.coverGutter)
5893 parentStyle += "margin-left: -" + widget.cm.display.gutters.offsetWidth + "px;"; 6708 parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" ;
5894 if (widget.noHScroll) 6709 if (widget.noHScroll)
5895 parentStyle += "width: " + widget.cm.display.wrapper.clientWidth + "px;" ; 6710 parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
5896 removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, parentStyle)); 6711 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, p arentStyle));
5897 } 6712 }
5898 return widget.height = widget.node.offsetHeight; 6713 return widget.height = widget.node.parentNode.offsetHeight;
5899 } 6714 }
5900 6715
5901 function addLineWidget(cm, handle, node, options) { 6716 function addLineWidget(doc, handle, node, options) {
5902 var widget = new LineWidget(cm, node, options); 6717 var widget = new LineWidget(doc, node, options);
5903 if (widget.noHScroll) cm.display.alignWidgets = true; 6718 var cm = doc.cm;
5904 changeLine(cm.doc, handle, "widget", function(line) { 6719 if (cm && widget.noHScroll) cm.display.alignWidgets = true;
6720 changeLine(doc, handle, "widget", function(line) {
5905 var widgets = line.widgets || (line.widgets = []); 6721 var widgets = line.widgets || (line.widgets = []);
5906 if (widget.insertAt == null) widgets.push(widget); 6722 if (widget.insertAt == null) widgets.push(widget);
5907 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insert At)), 0, widget); 6723 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insert At)), 0, widget);
5908 widget.line = line; 6724 widget.line = line;
5909 if (!lineIsHidden(cm.doc, line)) { 6725 if (cm && !lineIsHidden(doc, line)) {
5910 var aboveVisible = heightAtLine(line) < cm.doc.scrollTop; 6726 var aboveVisible = heightAtLine(line) < doc.scrollTop;
5911 updateLineHeight(line, line.height + widgetHeight(widget)); 6727 updateLineHeight(line, line.height + widgetHeight(widget));
5912 if (aboveVisible) addToScrollPos(cm, null, widget.height); 6728 if (aboveVisible) addToScrollPos(cm, null, widget.height);
5913 cm.curOp.forceUpdate = true; 6729 cm.curOp.forceUpdate = true;
5914 } 6730 }
5915 return true; 6731 return true;
5916 }); 6732 });
5917 return widget; 6733 return widget;
5918 } 6734 }
5919 6735
5920 // LINE DATA STRUCTURE 6736 // LINE DATA STRUCTURE
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
6076 } 6892 }
6077 } 6893 }
6078 }, lineClasses); 6894 }, lineClasses);
6079 } 6895 }
6080 6896
6081 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}; 6897 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
6082 } 6898 }
6083 6899
6084 function getLineStyles(cm, line, updateFrontier) { 6900 function getLineStyles(cm, line, updateFrontier) {
6085 if (!line.styles || line.styles[0] != cm.state.modeGen) { 6901 if (!line.styles || line.styles[0] != cm.state.modeGen) {
6086 var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); 6902 var state = getStateBefore(cm, lineNo(line));
6903 var result = highlightLine(cm, line, line.text.length > cm.options.maxHigh lightLength ? copyState(cm.doc.mode, state) : state);
6904 line.stateAfter = state;
6087 line.styles = result.styles; 6905 line.styles = result.styles;
6088 if (result.classes) line.styleClasses = result.classes; 6906 if (result.classes) line.styleClasses = result.classes;
6089 else if (line.styleClasses) line.styleClasses = null; 6907 else if (line.styleClasses) line.styleClasses = null;
6090 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++; 6908 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
6091 } 6909 }
6092 return line.styles; 6910 return line.styles;
6093 } 6911 }
6094 6912
6095 // Lightweight form of highlight -- proceed over this line and 6913 // Lightweight form of highlight -- proceed over this line and
6096 // update state, but don't save a style array. Used for lines that 6914 // update state, but don't save a style array. Used for lines that
6097 // aren't currently visible. 6915 // aren't currently visible.
6098 function processLine(cm, text, state, startAt) { 6916 function processLine(cm, text, state, startAt) {
6099 var mode = cm.doc.mode; 6917 var mode = cm.doc.mode;
6100 var stream = new StringStream(text, cm.options.tabSize); 6918 var stream = new StringStream(text, cm.options.tabSize);
6101 stream.start = stream.pos = startAt || 0; 6919 stream.start = stream.pos = startAt || 0;
6102 if (text == "") callBlankLine(mode, state); 6920 if (text == "") callBlankLine(mode, state);
6103 while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) { 6921 while (!stream.eol()) {
6104 readToken(mode, stream, state); 6922 readToken(mode, stream, state);
6105 stream.start = stream.pos; 6923 stream.start = stream.pos;
6106 } 6924 }
6107 } 6925 }
6108 6926
6109 // Convert a style as returned by a mode (either null, or a string 6927 // Convert a style as returned by a mode (either null, or a string
6110 // containing one or more styles) to a CSS style. This is cached, 6928 // containing one or more styles) to a CSS style. This is cached,
6111 // and also looks for line-wide styles. 6929 // and also looks for line-wide styles.
6112 var styleToClassCache = {}, styleToClassCacheWithMode = {}; 6930 var styleToClassCache = {}, styleToClassCacheWithMode = {};
6113 function interpretTokenStyle(style, options) { 6931 function interpretTokenStyle(style, options) {
6114 if (!style || /^\s*$/.test(style)) return null; 6932 if (!style || /^\s*$/.test(style)) return null;
6115 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassC ache; 6933 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassC ache;
6116 return cache[style] || 6934 return cache[style] ||
6117 (cache[style] = style.replace(/\S+/g, "cm-$&")); 6935 (cache[style] = style.replace(/\S+/g, "cm-$&"));
6118 } 6936 }
6119 6937
6120 // Render the DOM representation of the text of a line. Also builds 6938 // Render the DOM representation of the text of a line. Also builds
6121 // up a 'line map', which points at the DOM nodes that represent 6939 // up a 'line map', which points at the DOM nodes that represent
6122 // specific stretches of text, and is used by the measuring code. 6940 // specific stretches of text, and is used by the measuring code.
6123 // The returned object contains the DOM node, this map, and 6941 // The returned object contains the DOM node, this map, and
6124 // information about line-wide styles that were set by the mode. 6942 // information about line-wide styles that were set by the mode.
6125 function buildLineContent(cm, lineView) { 6943 function buildLineContent(cm, lineView) {
6126 // The padding-right forces the element to have a 'border', which 6944 // The padding-right forces the element to have a 'border', which
6127 // is needed on Webkit to be able to get line-level bounding 6945 // is needed on Webkit to be able to get line-level bounding
6128 // rectangles for it (in measureChar). 6946 // rectangles for it (in measureChar).
6129 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null) ; 6947 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null) ;
6130 var builder = {pre: elt("pre", [content]), content: content, col: 0, pos: 0, cm: cm}; 6948 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: conte nt,
6949 col: 0, pos: 0, cm: cm,
6950 trailingSpace: false,
6951 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
6131 lineView.measure = {}; 6952 lineView.measure = {};
6132 6953
6133 // Iterate over the logical lines that make up this visual line. 6954 // Iterate over the logical lines that make up this visual line.
6134 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { 6955 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
6135 var line = i ? lineView.rest[i - 1] : lineView.line, order; 6956 var line = i ? lineView.rest[i - 1] : lineView.line, order;
6136 builder.pos = 0; 6957 builder.pos = 0;
6137 builder.addToken = buildToken; 6958 builder.addToken = buildToken;
6138 // Optionally wire in some hacks into the token-rendering 6959 // Optionally wire in some hacks into the token-rendering
6139 // algorithm, to deal with browser quirks. 6960 // algorithm, to deal with browser quirks.
6140 if ((ie || webkit) && cm.getOption("lineWrapping"))
6141 builder.addToken = buildTokenSplitSpaces(builder.addToken);
6142 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) 6961 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
6143 builder.addToken = buildTokenBadBidi(builder.addToken, order); 6962 builder.addToken = buildTokenBadBidi(builder.addToken, order);
6144 builder.map = []; 6963 builder.map = [];
6145 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineN o(line); 6964 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineN o(line);
6146 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpda te)); 6965 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpda te));
6147 if (line.styleClasses) { 6966 if (line.styleClasses) {
6148 if (line.styleClasses.bgClass) 6967 if (line.styleClasses.bgClass)
6149 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgCla ss || ""); 6968 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgCla ss || "");
6150 if (line.styleClasses.textClass) 6969 if (line.styleClasses.textClass)
6151 builder.textClass = joinClasses(line.styleClasses.textClass, builder.t extClass || ""); 6970 builder.textClass = joinClasses(line.styleClasses.textClass, builder.t extClass || "");
6152 } 6971 }
6153 6972
6154 // Ensure at least a single node is present, for measuring. 6973 // Ensure at least a single node is present, for measuring.
6155 if (builder.map.length == 0) 6974 if (builder.map.length == 0)
6156 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.d isplay.measure))); 6975 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.d isplay.measure)));
6157 6976
6158 // Store the map and a cache object for the current logical line 6977 // Store the map and a cache object for the current logical line
6159 if (i == 0) { 6978 if (i == 0) {
6160 lineView.measure.map = builder.map; 6979 lineView.measure.map = builder.map;
6161 lineView.measure.cache = {}; 6980 lineView.measure.cache = {};
6162 } else { 6981 } else {
6163 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map ); 6982 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map );
6164 (lineView.measure.caches || (lineView.measure.caches = [])).push({}); 6983 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
6165 } 6984 }
6166 } 6985 }
6167 6986
6168 // See issue #2901 6987 // See issue #2901
6169 if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className)) 6988 if (webkit) {
6170 builder.content.className = "cm-tab-wrap-hack"; 6989 var last = builder.content.lastChild
6990 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.query Selector(".cm-tab")))
6991 builder.content.className = "cm-tab-wrap-hack";
6992 }
6171 6993
6172 signal(cm, "renderLine", cm, lineView.line, builder.pre); 6994 signal(cm, "renderLine", cm, lineView.line, builder.pre);
6173 if (builder.pre.className) 6995 if (builder.pre.className)
6174 builder.textClass = joinClasses(builder.pre.className, builder.textClass | | ""); 6996 builder.textClass = joinClasses(builder.pre.className, builder.textClass | | "");
6175 6997
6176 return builder; 6998 return builder;
6177 } 6999 }
6178 7000
6179 function defaultSpecialCharPlaceholder(ch) { 7001 function defaultSpecialCharPlaceholder(ch) {
6180 var token = elt("span", "\u2022", "cm-invalidchar"); 7002 var token = elt("span", "\u2022", "cm-invalidchar");
6181 token.title = "\\u" + ch.charCodeAt(0).toString(16); 7003 token.title = "\\u" + ch.charCodeAt(0).toString(16);
7004 token.setAttribute("aria-label", token.title);
6182 return token; 7005 return token;
6183 } 7006 }
6184 7007
6185 // Build up the DOM representation for a single token, and add it to 7008 // Build up the DOM representation for a single token, and add it to
6186 // the line map. Takes care to render special characters separately. 7009 // the line map. Takes care to render special characters separately.
6187 function buildToken(builder, text, style, startStyle, endStyle, title, css) { 7010 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
6188 if (!text) return; 7011 if (!text) return;
6189 var special = builder.cm.options.specialChars, mustWrap = false; 7012 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSp ace) : text
7013 var special = builder.cm.state.specialChars, mustWrap = false;
6190 if (!special.test(text)) { 7014 if (!special.test(text)) {
6191 builder.col += text.length; 7015 builder.col += text.length;
6192 var content = document.createTextNode(text); 7016 var content = document.createTextNode(displayText);
6193 builder.map.push(builder.pos, builder.pos + text.length, content); 7017 builder.map.push(builder.pos, builder.pos + text.length, content);
6194 if (ie && ie_version < 9) mustWrap = true; 7018 if (ie && ie_version < 9) mustWrap = true;
6195 builder.pos += text.length; 7019 builder.pos += text.length;
6196 } else { 7020 } else {
6197 var content = document.createDocumentFragment(), pos = 0; 7021 var content = document.createDocumentFragment(), pos = 0;
6198 while (true) { 7022 while (true) {
6199 special.lastIndex = pos; 7023 special.lastIndex = pos;
6200 var m = special.exec(text); 7024 var m = special.exec(text);
6201 var skipped = m ? m.index - pos : text.length - pos; 7025 var skipped = m ? m.index - pos : text.length - pos;
6202 if (skipped) { 7026 if (skipped) {
6203 var txt = document.createTextNode(text.slice(pos, pos + skipped)); 7027 var txt = document.createTextNode(displayText.slice(pos, pos + skipped ));
6204 if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); 7028 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6205 else content.appendChild(txt); 7029 else content.appendChild(txt);
6206 builder.map.push(builder.pos, builder.pos + skipped, txt); 7030 builder.map.push(builder.pos, builder.pos + skipped, txt);
6207 builder.col += skipped; 7031 builder.col += skipped;
6208 builder.pos += skipped; 7032 builder.pos += skipped;
6209 } 7033 }
6210 if (!m) break; 7034 if (!m) break;
6211 pos += skipped + 1; 7035 pos += skipped + 1;
6212 if (m[0] == "\t") { 7036 if (m[0] == "\t") {
6213 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder .col % tabSize; 7037 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder .col % tabSize;
6214 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab" )); 7038 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab" ));
7039 txt.setAttribute("role", "presentation");
7040 txt.setAttribute("cm-text", "\t");
6215 builder.col += tabWidth; 7041 builder.col += tabWidth;
7042 } else if (m[0] == "\r" || m[0] == "\n") {
7043 var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\ u2424", "cm-invalidchar"));
7044 txt.setAttribute("cm-text", m[0]);
7045 builder.col += 1;
6216 } else { 7046 } else {
6217 var txt = builder.cm.options.specialCharPlaceholder(m[0]); 7047 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
7048 txt.setAttribute("cm-text", m[0]);
6218 if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); 7049 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
6219 else content.appendChild(txt); 7050 else content.appendChild(txt);
6220 builder.col += 1; 7051 builder.col += 1;
6221 } 7052 }
6222 builder.map.push(builder.pos, builder.pos + 1, txt); 7053 builder.map.push(builder.pos, builder.pos + 1, txt);
6223 builder.pos++; 7054 builder.pos++;
6224 } 7055 }
6225 } 7056 }
7057 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32
6226 if (style || startStyle || endStyle || mustWrap || css) { 7058 if (style || startStyle || endStyle || mustWrap || css) {
6227 var fullStyle = style || ""; 7059 var fullStyle = style || "";
6228 if (startStyle) fullStyle += startStyle; 7060 if (startStyle) fullStyle += startStyle;
6229 if (endStyle) fullStyle += endStyle; 7061 if (endStyle) fullStyle += endStyle;
6230 var token = elt("span", [content], fullStyle, css); 7062 var token = elt("span", [content], fullStyle, css);
6231 if (title) token.title = title; 7063 if (title) token.title = title;
6232 return builder.content.appendChild(token); 7064 return builder.content.appendChild(token);
6233 } 7065 }
6234 builder.content.appendChild(content); 7066 builder.content.appendChild(content);
6235 } 7067 }
6236 7068
6237 function buildTokenSplitSpaces(inner) { 7069 function splitSpaces(text, trailingBefore) {
6238 function split(old) { 7070 if (text.length > 1 && !/ /.test(text)) return text
6239 var out = " "; 7071 var spaceBefore = trailingBefore, result = ""
6240 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0"; 7072 for (var i = 0; i < text.length; i++) {
6241 out += " "; 7073 var ch = text.charAt(i)
6242 return out; 7074 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
7075 ch = "\u00a0"
7076 result += ch
7077 spaceBefore = ch == " "
6243 } 7078 }
6244 return function(builder, text, style, startStyle, endStyle, title) { 7079 return result
6245 inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
6246 };
6247 } 7080 }
6248 7081
6249 // Work around nonsense dimensions being reported for stretches of 7082 // Work around nonsense dimensions being reported for stretches of
6250 // right-to-left text. 7083 // right-to-left text.
6251 function buildTokenBadBidi(inner, order) { 7084 function buildTokenBadBidi(inner, order) {
6252 return function(builder, text, style, startStyle, endStyle, title) { 7085 return function(builder, text, style, startStyle, endStyle, title, css) {
6253 style = style ? style + " cm-force-border" : "cm-force-border"; 7086 style = style ? style + " cm-force-border" : "cm-force-border";
6254 var start = builder.pos, end = start + text.length; 7087 var start = builder.pos, end = start + text.length;
6255 for (;;) { 7088 for (;;) {
6256 // Find the part that overlaps with the start of this text 7089 // Find the part that overlaps with the start of this text
6257 for (var i = 0; i < order.length; i++) { 7090 for (var i = 0; i < order.length; i++) {
6258 var part = order[i]; 7091 var part = order[i];
6259 if (part.to > start && part.from <= start) break; 7092 if (part.to > start && part.from <= start) break;
6260 } 7093 }
6261 if (part.to >= end) return inner(builder, text, style, startStyle, endSt yle, title); 7094 if (part.to >= end) return inner(builder, text, style, startStyle, endSt yle, title, css);
6262 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title); 7095 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
6263 startStyle = null; 7096 startStyle = null;
6264 text = text.slice(part.to - start); 7097 text = text.slice(part.to - start);
6265 start = part.to; 7098 start = part.to;
6266 } 7099 }
6267 }; 7100 };
6268 } 7101 }
6269 7102
6270 function buildCollapsedSpan(builder, size, marker, ignoreWidget) { 7103 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
6271 var widget = !ignoreWidget && marker.widgetNode; 7104 var widget = !ignoreWidget && marker.widgetNode;
7105 if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
7106 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
7107 if (!widget)
7108 widget = builder.content.appendChild(document.createElement("span"));
7109 widget.setAttribute("cm-marker", marker.id);
7110 }
6272 if (widget) { 7111 if (widget) {
6273 builder.map.push(builder.pos, builder.pos + size, widget); 7112 builder.cm.display.input.setUneditable(widget);
6274 builder.content.appendChild(widget); 7113 builder.content.appendChild(widget);
6275 } 7114 }
6276 builder.pos += size; 7115 builder.pos += size;
7116 builder.trailingSpace = false
6277 } 7117 }
6278 7118
6279 // Outputs a number of spans to make up a line, taking highlighting 7119 // Outputs a number of spans to make up a line, taking highlighting
6280 // and marked text into account. 7120 // and marked text into account.
6281 function insertLineContent(line, builder, styles) { 7121 function insertLineContent(line, builder, styles) {
6282 var spans = line.markedSpans, allText = line.text, at = 0; 7122 var spans = line.markedSpans, allText = line.text, at = 0;
6283 if (!spans) { 7123 if (!spans) {
6284 for (var i = 1; i < styles.length; i+=2) 7124 for (var i = 1; i < styles.length; i+=2)
6285 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTo kenStyle(styles[i+1], builder.cm.options)); 7125 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTo kenStyle(styles[i+1], builder.cm.options));
6286 return; 7126 return;
6287 } 7127 }
6288 7128
6289 var len = allText.length, pos = 0, i = 1, text = "", style, css; 7129 var len = allText.length, pos = 0, i = 1, text = "", style, css;
6290 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapse d; 7130 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapse d;
6291 for (;;) { 7131 for (;;) {
6292 if (nextChange == pos) { // Update current marker set 7132 if (nextChange == pos) { // Update current marker set
6293 spanStyle = spanEndStyle = spanStartStyle = title = css = ""; 7133 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
6294 collapsed = null; nextChange = Infinity; 7134 collapsed = null; nextChange = Infinity;
6295 var foundBookmarks = []; 7135 var foundBookmarks = [], endStyles
6296 for (var j = 0; j < spans.length; ++j) { 7136 for (var j = 0; j < spans.length; ++j) {
6297 var sp = spans[j], m = sp.marker; 7137 var sp = spans[j], m = sp.marker;
6298 if (sp.from <= pos && (sp.to == null || sp.to > pos)) { 7138 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
6299 if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanE ndStyle = ""; } 7139 foundBookmarks.push(m);
7140 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collap sed && sp.to == pos && sp.from == pos)) {
7141 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
7142 nextChange = sp.to;
7143 spanEndStyle = "";
7144 }
6300 if (m.className) spanStyle += " " + m.className; 7145 if (m.className) spanStyle += " " + m.className;
6301 if (m.css) css = m.css; 7146 if (m.css) css = (css ? css + ";" : "") + m.css;
6302 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startS tyle; 7147 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startS tyle;
6303 if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endSt yle; 7148 if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [] )).push(m.endStyle, sp.to)
6304 if (m.title && !title) title = m.title; 7149 if (m.title && !title) title = m.title;
6305 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed. marker, m) < 0)) 7150 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed. marker, m) < 0))
6306 collapsed = sp; 7151 collapsed = sp;
6307 } else if (sp.from > pos && nextChange > sp.from) { 7152 } else if (sp.from > pos && nextChange > sp.from) {
6308 nextChange = sp.from; 7153 nextChange = sp.from;
6309 } 7154 }
6310 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) foundBookm arks.push(m);
6311 } 7155 }
7156 if (endStyles) for (var j = 0; j < endStyles.length; j += 2)
7157 if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j]
7158
7159 if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookma rks.length; ++j)
7160 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
6312 if (collapsed && (collapsed.from || 0) == pos) { 7161 if (collapsed && (collapsed.from || 0) == pos) {
6313 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapse d.to) - pos, 7162 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapse d.to) - pos,
6314 collapsed.marker, collapsed.from == null); 7163 collapsed.marker, collapsed.from == null);
6315 if (collapsed.to == null) return; 7164 if (collapsed.to == null) return;
7165 if (collapsed.to == pos) collapsed = false;
6316 } 7166 }
6317 if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookma rks.length; ++j)
6318 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
6319 } 7167 }
6320 if (pos >= len) break; 7168 if (pos >= len) break;
6321 7169
6322 var upto = Math.min(len, nextChange); 7170 var upto = Math.min(len, nextChange);
6323 while (true) { 7171 while (true) {
6324 if (text) { 7172 if (text) {
6325 var end = pos + text.length; 7173 var end = pos + text.length;
6326 if (!collapsed) { 7174 if (!collapsed) {
6327 var tokenText = end > upto ? text.slice(0, upto - pos) : text; 7175 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
6328 builder.addToken(builder, tokenText, style ? style + spanStyle : spa nStyle, 7176 builder.addToken(builder, tokenText, style ? style + spanStyle : spa nStyle,
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
6494 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(l ines); 7342 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(l ines);
6495 }, 7343 },
6496 insertInner: function(at, lines, height) { 7344 insertInner: function(at, lines, height) {
6497 this.size += lines.length; 7345 this.size += lines.length;
6498 this.height += height; 7346 this.height += height;
6499 for (var i = 0; i < this.children.length; ++i) { 7347 for (var i = 0; i < this.children.length; ++i) {
6500 var child = this.children[i], sz = child.chunkSize(); 7348 var child = this.children[i], sz = child.chunkSize();
6501 if (at <= sz) { 7349 if (at <= sz) {
6502 child.insertInner(at, lines, height); 7350 child.insertInner(at, lines, height);
6503 if (child.lines && child.lines.length > 50) { 7351 if (child.lines && child.lines.length > 50) {
6504 while (child.lines.length > 50) { 7352 // To avoid memory thrashing when child.lines is huge (e.g. first vi ew of a large file), it's never spliced.
6505 var spilled = child.lines.splice(child.lines.length - 25, 25); 7353 // Instead, small slices are taken. They're taken in order because s equential memory accesses are fastest.
6506 var newleaf = new LeafChunk(spilled); 7354 var remaining = child.lines.length % 25 + 25
6507 child.height -= newleaf.height; 7355 for (var pos = remaining; pos < child.lines.length;) {
6508 this.children.splice(i + 1, 0, newleaf); 7356 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
6509 newleaf.parent = this; 7357 child.height -= leaf.height;
7358 this.children.splice(++i, 0, leaf);
7359 leaf.parent = this;
6510 } 7360 }
7361 child.lines = child.lines.slice(0, remaining);
6511 this.maybeSpill(); 7362 this.maybeSpill();
6512 } 7363 }
6513 break; 7364 break;
6514 } 7365 }
6515 at -= sz; 7366 at -= sz;
6516 } 7367 }
6517 }, 7368 },
6518 // When a node has grown, check whether it should be split. 7369 // When a node has grown, check whether it should be split.
6519 maybeSpill: function() { 7370 maybeSpill: function() {
6520 if (this.children.length <= 10) return; 7371 if (this.children.length <= 10) return;
6521 var me = this; 7372 var me = this;
6522 do { 7373 do {
6523 var spilled = me.children.splice(me.children.length - 5, 5); 7374 var spilled = me.children.splice(me.children.length - 5, 5);
6524 var sibling = new BranchChunk(spilled); 7375 var sibling = new BranchChunk(spilled);
6525 if (!me.parent) { // Become the parent node 7376 if (!me.parent) { // Become the parent node
6526 var copy = new BranchChunk(me.children); 7377 var copy = new BranchChunk(me.children);
6527 copy.parent = me; 7378 copy.parent = me;
6528 me.children = [copy, sibling]; 7379 me.children = [copy, sibling];
6529 me = copy; 7380 me = copy;
6530 } else { 7381 } else {
6531 me.size -= sibling.size; 7382 me.size -= sibling.size;
6532 me.height -= sibling.height; 7383 me.height -= sibling.height;
6533 var myIndex = indexOf(me.parent.children, me); 7384 var myIndex = indexOf(me.parent.children, me);
6534 me.parent.children.splice(myIndex + 1, 0, sibling); 7385 me.parent.children.splice(myIndex + 1, 0, sibling);
6535 } 7386 }
6536 sibling.parent = me.parent; 7387 sibling.parent = me.parent;
6537 } while (me.children.length > 10); 7388 } while (me.children.length > 10);
6538 me.parent.maybeSpill(); 7389 me.parent.maybeSpill();
6539 }, 7390 },
6540 iterN: function(at, n, op) { 7391 iterN: function(at, n, op) {
6541 for (var i = 0; i < this.children.length; ++i) { 7392 for (var i = 0; i < this.children.length; ++i) {
6542 var child = this.children[i], sz = child.chunkSize(); 7393 var child = this.children[i], sz = child.chunkSize();
6543 if (at < sz) { 7394 if (at < sz) {
6544 var used = Math.min(n, sz - at); 7395 var used = Math.min(n, sz - at);
6545 if (child.iterN(at, used, op)) return true; 7396 if (child.iterN(at, used, op)) return true;
6546 if ((n -= used) == 0) break; 7397 if ((n -= used) == 0) break;
6547 at = 0; 7398 at = 0;
6548 } else at -= sz; 7399 } else at -= sz;
6549 } 7400 }
6550 } 7401 }
6551 }; 7402 };
6552 7403
6553 var nextDocId = 0; 7404 var nextDocId = 0;
6554 var Doc = CodeMirror.Doc = function(text, mode, firstLine) { 7405 var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
6555 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); 7406 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
6556 if (firstLine == null) firstLine = 0; 7407 if (firstLine == null) firstLine = 0;
6557 7408
6558 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); 7409 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
6559 this.first = firstLine; 7410 this.first = firstLine;
6560 this.scrollTop = this.scrollLeft = 0; 7411 this.scrollTop = this.scrollLeft = 0;
6561 this.cantEdit = false; 7412 this.cantEdit = false;
6562 this.cleanGeneration = 1; 7413 this.cleanGeneration = 1;
6563 this.frontier = firstLine; 7414 this.frontier = firstLine;
6564 var start = Pos(firstLine, 0); 7415 var start = Pos(firstLine, 0);
6565 this.sel = simpleSelection(start); 7416 this.sel = simpleSelection(start);
6566 this.history = new History(null); 7417 this.history = new History(null);
6567 this.id = ++nextDocId; 7418 this.id = ++nextDocId;
6568 this.modeOption = mode; 7419 this.modeOption = mode;
7420 this.lineSep = lineSep;
7421 this.extend = false;
6569 7422
6570 if (typeof text == "string") text = splitLines(text); 7423 if (typeof text == "string") text = this.splitLines(text);
6571 updateDoc(this, {from: start, to: start, text: text}); 7424 updateDoc(this, {from: start, to: start, text: text});
6572 setSelection(this, simpleSelection(start), sel_dontScroll); 7425 setSelection(this, simpleSelection(start), sel_dontScroll);
6573 }; 7426 };
6574 7427
6575 Doc.prototype = createObj(BranchChunk.prototype, { 7428 Doc.prototype = createObj(BranchChunk.prototype, {
6576 constructor: Doc, 7429 constructor: Doc,
6577 // Iterate over the document. Supports two forms -- with only one 7430 // Iterate over the document. Supports two forms -- with only one
6578 // argument, it calls that for each line in the document. With 7431 // argument, it calls that for each line in the document. With
6579 // three, it iterates over the range given by the first two (with 7432 // three, it iterates over the range given by the first two (with
6580 // the second being non-inclusive). 7433 // the second being non-inclusive).
6581 iter: function(from, to, op) { 7434 iter: function(from, to, op) {
6582 if (op) this.iterN(from - this.first, to - from, op); 7435 if (op) this.iterN(from - this.first, to - from, op);
6583 else this.iterN(this.first, this.first + this.size, from); 7436 else this.iterN(this.first, this.first + this.size, from);
6584 }, 7437 },
6585 7438
6586 // Non-public interface for adding and removing lines. 7439 // Non-public interface for adding and removing lines.
6587 insert: function(at, lines) { 7440 insert: function(at, lines) {
6588 var height = 0; 7441 var height = 0;
6589 for (var i = 0; i < lines.length; ++i) height += lines[i].height; 7442 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
6590 this.insertInner(at - this.first, lines, height); 7443 this.insertInner(at - this.first, lines, height);
6591 }, 7444 },
6592 remove: function(at, n) { this.removeInner(at - this.first, n); }, 7445 remove: function(at, n) { this.removeInner(at - this.first, n); },
6593 7446
6594 // From here, the methods are part of the public interface. Most 7447 // From here, the methods are part of the public interface. Most
6595 // are also available from CodeMirror (editor) instances. 7448 // are also available from CodeMirror (editor) instances.
6596 7449
6597 getValue: function(lineSep) { 7450 getValue: function(lineSep) {
6598 var lines = getLines(this, this.first, this.first + this.size); 7451 var lines = getLines(this, this.first, this.first + this.size);
6599 if (lineSep === false) return lines; 7452 if (lineSep === false) return lines;
6600 return lines.join(lineSep || "\n"); 7453 return lines.join(lineSep || this.lineSeparator());
6601 }, 7454 },
6602 setValue: docMethodOp(function(code) { 7455 setValue: docMethodOp(function(code) {
6603 var top = Pos(this.first, 0), last = this.first + this.size - 1; 7456 var top = Pos(this.first, 0), last = this.first + this.size - 1;
6604 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length ), 7457 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length ),
6605 text: splitLines(code), origin: "setValue", full: true}, true); 7458 text: this.splitLines(code), origin: "setValue", full: t rue}, true);
6606 setSelection(this, simpleSelection(top)); 7459 setSelection(this, simpleSelection(top));
6607 }), 7460 }),
6608 replaceRange: function(code, from, to, origin) { 7461 replaceRange: function(code, from, to, origin) {
6609 from = clipPos(this, from); 7462 from = clipPos(this, from);
6610 to = to ? clipPos(this, to) : from; 7463 to = to ? clipPos(this, to) : from;
6611 replaceRange(this, code, from, to, origin); 7464 replaceRange(this, code, from, to, origin);
6612 }, 7465 },
6613 getRange: function(from, to, lineSep) { 7466 getRange: function(from, to, lineSep) {
6614 var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); 7467 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
6615 if (lineSep === false) return lines; 7468 if (lineSep === false) return lines;
6616 return lines.join(lineSep || "\n"); 7469 return lines.join(lineSep || this.lineSeparator());
6617 }, 7470 },
6618 7471
6619 getLine: function(line) {var l = this.getLineHandle(line); return l && l.tex t;}, 7472 getLine: function(line) {var l = this.getLineHandle(line); return l && l.tex t;},
6620 7473
6621 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, 7474 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
6622 getLineNumber: function(line) {return lineNo(line);}, 7475 getLineNumber: function(line) {return lineNo(line);},
6623 7476
6624 getLineHandleVisualStart: function(line) { 7477 getLineHandleVisualStart: function(line) {
6625 if (typeof line == "number") line = getLine(this, line); 7478 if (typeof line == "number") line = getLine(this, line);
6626 return visualLine(line); 7479 return visualLine(line);
(...skipping 19 matching lines...) Expand all
6646 setCursor: docMethodOp(function(line, ch, options) { 7499 setCursor: docMethodOp(function(line, ch, options) {
6647 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); 7500 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
6648 }), 7501 }),
6649 setSelection: docMethodOp(function(anchor, head, options) { 7502 setSelection: docMethodOp(function(anchor, head, options) {
6650 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anch or), options); 7503 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anch or), options);
6651 }), 7504 }),
6652 extendSelection: docMethodOp(function(head, other, options) { 7505 extendSelection: docMethodOp(function(head, other, options) {
6653 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); 7506 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
6654 }), 7507 }),
6655 extendSelections: docMethodOp(function(heads, options) { 7508 extendSelections: docMethodOp(function(heads, options) {
6656 extendSelections(this, clipPosArray(this, heads, options)); 7509 extendSelections(this, clipPosArray(this, heads), options);
6657 }), 7510 }),
6658 extendSelectionsBy: docMethodOp(function(f, options) { 7511 extendSelectionsBy: docMethodOp(function(f, options) {
6659 extendSelections(this, map(this.sel.ranges, f), options); 7512 var heads = map(this.sel.ranges, f);
7513 extendSelections(this, clipPosArray(this, heads), options);
6660 }), 7514 }),
6661 setSelections: docMethodOp(function(ranges, primary, options) { 7515 setSelections: docMethodOp(function(ranges, primary, options) {
6662 if (!ranges.length) return; 7516 if (!ranges.length) return;
6663 for (var i = 0, out = []; i < ranges.length; i++) 7517 for (var i = 0, out = []; i < ranges.length; i++)
6664 out[i] = new Range(clipPos(this, ranges[i].anchor), 7518 out[i] = new Range(clipPos(this, ranges[i].anchor),
6665 clipPos(this, ranges[i].head)); 7519 clipPos(this, ranges[i].head));
6666 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIn dex); 7520 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIn dex);
6667 setSelection(this, normalizeSelection(out, primary), options); 7521 setSelection(this, normalizeSelection(out, primary), options);
6668 }), 7522 }),
6669 addSelection: docMethodOp(function(anchor, head, options) { 7523 addSelection: docMethodOp(function(anchor, head, options) {
6670 var ranges = this.sel.ranges.slice(0); 7524 var ranges = this.sel.ranges.slice(0);
6671 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor) )); 7525 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor) ));
6672 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options) ; 7526 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options) ;
6673 }), 7527 }),
6674 7528
6675 getSelection: function(lineSep) { 7529 getSelection: function(lineSep) {
6676 var ranges = this.sel.ranges, lines; 7530 var ranges = this.sel.ranges, lines;
6677 for (var i = 0; i < ranges.length; i++) { 7531 for (var i = 0; i < ranges.length; i++) {
6678 var sel = getBetween(this, ranges[i].from(), ranges[i].to()); 7532 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
6679 lines = lines ? lines.concat(sel) : sel; 7533 lines = lines ? lines.concat(sel) : sel;
6680 } 7534 }
6681 if (lineSep === false) return lines; 7535 if (lineSep === false) return lines;
6682 else return lines.join(lineSep || "\n"); 7536 else return lines.join(lineSep || this.lineSeparator());
6683 }, 7537 },
6684 getSelections: function(lineSep) { 7538 getSelections: function(lineSep) {
6685 var parts = [], ranges = this.sel.ranges; 7539 var parts = [], ranges = this.sel.ranges;
6686 for (var i = 0; i < ranges.length; i++) { 7540 for (var i = 0; i < ranges.length; i++) {
6687 var sel = getBetween(this, ranges[i].from(), ranges[i].to()); 7541 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
6688 if (lineSep !== false) sel = sel.join(lineSep || "\n"); 7542 if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator());
6689 parts[i] = sel; 7543 parts[i] = sel;
6690 } 7544 }
6691 return parts; 7545 return parts;
6692 }, 7546 },
6693 replaceSelection: function(code, collapse, origin) { 7547 replaceSelection: function(code, collapse, origin) {
6694 var dup = []; 7548 var dup = [];
6695 for (var i = 0; i < this.sel.ranges.length; i++) 7549 for (var i = 0; i < this.sel.ranges.length; i++)
6696 dup[i] = code; 7550 dup[i] = code;
6697 this.replaceSelections(dup, collapse, origin || "+input"); 7551 this.replaceSelections(dup, collapse, origin || "+input");
6698 }, 7552 },
6699 replaceSelections: docMethodOp(function(code, collapse, origin) { 7553 replaceSelections: docMethodOp(function(code, collapse, origin) {
6700 var changes = [], sel = this.sel; 7554 var changes = [], sel = this.sel;
6701 for (var i = 0; i < sel.ranges.length; i++) { 7555 for (var i = 0; i < sel.ranges.length; i++) {
6702 var range = sel.ranges[i]; 7556 var range = sel.ranges[i];
6703 changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[ i]), origin: origin}; 7557 changes[i] = {from: range.from(), to: range.to(), text: this.splitLines( code[i]), origin: origin};
6704 } 7558 }
6705 var newSel = collapse && collapse != "end" && computeReplacedSel(this, cha nges, collapse); 7559 var newSel = collapse && collapse != "end" && computeReplacedSel(this, cha nges, collapse);
6706 for (var i = changes.length - 1; i >= 0; i--) 7560 for (var i = changes.length - 1; i >= 0; i--)
6707 makeChange(this, changes[i]); 7561 makeChange(this, changes[i]);
6708 if (newSel) setSelectionReplaceHistory(this, newSel); 7562 if (newSel) setSelectionReplaceHistory(this, newSel);
6709 else if (this.cm) ensureCursorVisible(this.cm); 7563 else if (this.cm) ensureCursorVisible(this.cm);
6710 }), 7564 }),
6711 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), 7565 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
6712 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), 7566 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
6713 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", t rue);}), 7567 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", t rue);}),
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
6768 else { 7622 else {
6769 var found = cur.match(classTest(cls)); 7623 var found = cur.match(classTest(cls));
6770 if (!found) return false; 7624 if (!found) return false;
6771 var end = found.index + found[0].length; 7625 var end = found.index + found[0].length;
6772 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.l ength ? "" : " ") + cur.slice(end) || null; 7626 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.l ength ? "" : " ") + cur.slice(end) || null;
6773 } 7627 }
6774 return true; 7628 return true;
6775 }); 7629 });
6776 }), 7630 }),
6777 7631
7632 addLineWidget: docMethodOp(function(handle, node, options) {
7633 return addLineWidget(this, handle, node, options);
7634 }),
7635 removeLineWidget: function(widget) { widget.clear(); },
7636
6778 markText: function(from, to, options) { 7637 markText: function(from, to, options) {
6779 return markText(this, clipPos(this, from), clipPos(this, to), options, "ra nge"); 7638 return markText(this, clipPos(this, from), clipPos(this, to), options, opt ions && options.type || "range");
6780 }, 7639 },
6781 setBookmark: function(pos, options) { 7640 setBookmark: function(pos, options) {
6782 var realOpts = {replacedWith: options && (options.nodeType == null ? optio ns.widget : options), 7641 var realOpts = {replacedWith: options && (options.nodeType == null ? optio ns.widget : options),
6783 insertLeft: options && options.insertLeft, 7642 insertLeft: options && options.insertLeft,
6784 clearWhenEmpty: false, shared: options && options.shared}; 7643 clearWhenEmpty: false, shared: options && options.shared,
7644 handleMouseEvents: options && options.handleMouseEvents};
6785 pos = clipPos(this, pos); 7645 pos = clipPos(this, pos);
6786 return markText(this, pos, pos, realOpts, "bookmark"); 7646 return markText(this, pos, pos, realOpts, "bookmark");
6787 }, 7647 },
6788 findMarksAt: function(pos) { 7648 findMarksAt: function(pos) {
6789 pos = clipPos(this, pos); 7649 pos = clipPos(this, pos);
6790 var markers = [], spans = getLine(this, pos.line).markedSpans; 7650 var markers = [], spans = getLine(this, pos.line).markedSpans;
6791 if (spans) for (var i = 0; i < spans.length; ++i) { 7651 if (spans) for (var i = 0; i < spans.length; ++i) {
6792 var span = spans[i]; 7652 var span = spans[i];
6793 if ((span.from == null || span.from <= pos.ch) && 7653 if ((span.from == null || span.from <= pos.ch) &&
6794 (span.to == null || span.to >= pos.ch)) 7654 (span.to == null || span.to >= pos.ch))
6795 markers.push(span.marker.parent || span.marker); 7655 markers.push(span.marker.parent || span.marker);
6796 } 7656 }
6797 return markers; 7657 return markers;
6798 }, 7658 },
6799 findMarks: function(from, to, filter) { 7659 findMarks: function(from, to, filter) {
6800 from = clipPos(this, from); to = clipPos(this, to); 7660 from = clipPos(this, from); to = clipPos(this, to);
6801 var found = [], lineNo = from.line; 7661 var found = [], lineNo = from.line;
6802 this.iter(from.line, to.line + 1, function(line) { 7662 this.iter(from.line, to.line + 1, function(line) {
6803 var spans = line.markedSpans; 7663 var spans = line.markedSpans;
6804 if (spans) for (var i = 0; i < spans.length; i++) { 7664 if (spans) for (var i = 0; i < spans.length; i++) {
6805 var span = spans[i]; 7665 var span = spans[i];
6806 if (!(lineNo == from.line && from.ch > span.to || 7666 if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
6807 span.from == null && lineNo != from.line|| 7667 span.from == null && lineNo != from.line ||
6808 lineNo == to.line && span.from > to.ch) && 7668 span.from != null && lineNo == to.line && span.from >= to.ch) &&
6809 (!filter || filter(span.marker))) 7669 (!filter || filter(span.marker)))
6810 found.push(span.marker.parent || span.marker); 7670 found.push(span.marker.parent || span.marker);
6811 } 7671 }
6812 ++lineNo; 7672 ++lineNo;
6813 }); 7673 });
6814 return found; 7674 return found;
6815 }, 7675 },
6816 getAllMarks: function() { 7676 getAllMarks: function() {
6817 var markers = []; 7677 var markers = [];
6818 this.iter(function(line) { 7678 this.iter(function(line) {
6819 var sps = line.markedSpans; 7679 var sps = line.markedSpans;
6820 if (sps) for (var i = 0; i < sps.length; ++i) 7680 if (sps) for (var i = 0; i < sps.length; ++i)
6821 if (sps[i].from != null) markers.push(sps[i].marker); 7681 if (sps[i].from != null) markers.push(sps[i].marker);
6822 }); 7682 });
6823 return markers; 7683 return markers;
6824 }, 7684 },
6825 7685
6826 posFromIndex: function(off) { 7686 posFromIndex: function(off) {
6827 var ch, lineNo = this.first; 7687 var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
6828 this.iter(function(line) { 7688 this.iter(function(line) {
6829 var sz = line.text.length + 1; 7689 var sz = line.text.length + sepSize;
6830 if (sz > off) { ch = off; return true; } 7690 if (sz > off) { ch = off; return true; }
6831 off -= sz; 7691 off -= sz;
6832 ++lineNo; 7692 ++lineNo;
6833 }); 7693 });
6834 return clipPos(this, Pos(lineNo, ch)); 7694 return clipPos(this, Pos(lineNo, ch));
6835 }, 7695 },
6836 indexFromPos: function (coords) { 7696 indexFromPos: function (coords) {
6837 coords = clipPos(this, coords); 7697 coords = clipPos(this, coords);
6838 var index = coords.ch; 7698 var index = coords.ch;
6839 if (coords.line < this.first || coords.ch < 0) return 0; 7699 if (coords.line < this.first || coords.ch < 0) return 0;
7700 var sepSize = this.lineSeparator().length;
6840 this.iter(this.first, coords.line, function (line) { 7701 this.iter(this.first, coords.line, function (line) {
6841 index += line.text.length + 1; 7702 index += line.text.length + sepSize;
6842 }); 7703 });
6843 return index; 7704 return index;
6844 }, 7705 },
6845 7706
6846 copy: function(copyHistory) { 7707 copy: function(copyHistory) {
6847 var doc = new Doc(getLines(this, this.first, this.first + this.size), this .modeOption, this.first); 7708 var doc = new Doc(getLines(this, this.first, this.first + this.size),
7709 this.modeOption, this.first, this.lineSep);
6848 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; 7710 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
6849 doc.sel = this.sel; 7711 doc.sel = this.sel;
6850 doc.extend = false; 7712 doc.extend = false;
6851 if (copyHistory) { 7713 if (copyHistory) {
6852 doc.history.undoDepth = this.history.undoDepth; 7714 doc.history.undoDepth = this.history.undoDepth;
6853 doc.setHistory(this.getHistory()); 7715 doc.setHistory(this.getHistory());
6854 } 7716 }
6855 return doc; 7717 return doc;
6856 }, 7718 },
6857 7719
6858 linkedDoc: function(options) { 7720 linkedDoc: function(options) {
6859 if (!options) options = {}; 7721 if (!options) options = {};
6860 var from = this.first, to = this.first + this.size; 7722 var from = this.first, to = this.first + this.size;
6861 if (options.from != null && options.from > from) from = options.from; 7723 if (options.from != null && options.from > from) from = options.from;
6862 if (options.to != null && options.to < to) to = options.to; 7724 if (options.to != null && options.to < to) to = options.to;
6863 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOpti on, from); 7725 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOpti on, from, this.lineSep);
6864 if (options.sharedHist) copy.history = this.history; 7726 if (options.sharedHist) copy.history = this.history;
6865 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.s haredHist}); 7727 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.s haredHist});
6866 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist} ]; 7728 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist} ];
6867 copySharedMarkers(copy, findSharedMarkers(this)); 7729 copySharedMarkers(copy, findSharedMarkers(this));
6868 return copy; 7730 return copy;
6869 }, 7731 },
6870 unlinkDoc: function(other) { 7732 unlinkDoc: function(other) {
6871 if (other instanceof CodeMirror) other = other.doc; 7733 if (other instanceof CodeMirror) other = other.doc;
6872 if (this.linked) for (var i = 0; i < this.linked.length; ++i) { 7734 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
6873 var link = this.linked[i]; 7735 var link = this.linked[i];
6874 if (link.doc != other) continue; 7736 if (link.doc != other) continue;
6875 this.linked.splice(i, 1); 7737 this.linked.splice(i, 1);
6876 other.unlinkDoc(this); 7738 other.unlinkDoc(this);
6877 detachSharedMarkers(findSharedMarkers(this)); 7739 detachSharedMarkers(findSharedMarkers(this));
6878 break; 7740 break;
6879 } 7741 }
6880 // If the histories were shared, split them again 7742 // If the histories were shared, split them again
6881 if (other.history == this.history) { 7743 if (other.history == this.history) {
6882 var splitIds = [other.id]; 7744 var splitIds = [other.id];
6883 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true); 7745 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
6884 other.history = new History(null); 7746 other.history = new History(null);
6885 other.history.done = copyHistoryArray(this.history.done, splitIds); 7747 other.history.done = copyHistoryArray(this.history.done, splitIds);
6886 other.history.undone = copyHistoryArray(this.history.undone, splitIds); 7748 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
6887 } 7749 }
6888 }, 7750 },
6889 iterLinkedDocs: function(f) {linkedDocs(this, f);}, 7751 iterLinkedDocs: function(f) {linkedDocs(this, f);},
6890 7752
6891 getMode: function() {return this.mode;}, 7753 getMode: function() {return this.mode;},
6892 getEditor: function() {return this.cm;} 7754 getEditor: function() {return this.cm;},
7755
7756 splitLines: function(str) {
7757 if (this.lineSep) return str.split(this.lineSep);
7758 return splitLinesAuto(str);
7759 },
7760 lineSeparator: function() { return this.lineSep || "\n"; }
6893 }); 7761 });
6894 7762
6895 // Public alias. 7763 // Public alias.
6896 Doc.prototype.eachLine = Doc.prototype.iter; 7764 Doc.prototype.eachLine = Doc.prototype.iter;
6897 7765
6898 // Set up methods on CodeMirror's prototype to redirect to the editor's docume nt. 7766 // Set up methods on CodeMirror's prototype to redirect to the editor's docume nt.
6899 var dontDelegate = "iter insert remove copy getEditor".split(" "); 7767 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
6900 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && inde xOf(dontDelegate, prop) < 0) 7768 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && inde xOf(dontDelegate, prop) < 0)
6901 CodeMirror.prototype[prop] = (function(method) { 7769 CodeMirror.prototype[prop] = (function(method) {
6902 return function() {return method.apply(this.doc, arguments);}; 7770 return function() {return method.apply(this.doc, arguments);};
6903 })(Doc.prototype[prop]); 7771 })(Doc.prototype[prop]);
6904 7772
6905 eventMixin(Doc); 7773 eventMixin(Doc);
6906 7774
6907 // Call f for all linked documents. 7775 // Call f for all linked documents.
6908 function linkedDocs(doc, f, sharedHistOnly) { 7776 function linkedDocs(doc, f, sharedHistOnly) {
6909 function propagate(doc, skip, sharedHist) { 7777 function propagate(doc, skip, sharedHist) {
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after
7322 emitter.addEventListener(type, f, false); 8190 emitter.addEventListener(type, f, false);
7323 else if (emitter.attachEvent) 8191 else if (emitter.attachEvent)
7324 emitter.attachEvent("on" + type, f); 8192 emitter.attachEvent("on" + type, f);
7325 else { 8193 else {
7326 var map = emitter._handlers || (emitter._handlers = {}); 8194 var map = emitter._handlers || (emitter._handlers = {});
7327 var arr = map[type] || (map[type] = []); 8195 var arr = map[type] || (map[type] = []);
7328 arr.push(f); 8196 arr.push(f);
7329 } 8197 }
7330 }; 8198 };
7331 8199
8200 var noHandlers = []
8201 function getHandlers(emitter, type, copy) {
8202 var arr = emitter._handlers && emitter._handlers[type]
8203 if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
8204 else return arr || noHandlers
8205 }
8206
7332 var off = CodeMirror.off = function(emitter, type, f) { 8207 var off = CodeMirror.off = function(emitter, type, f) {
7333 if (emitter.removeEventListener) 8208 if (emitter.removeEventListener)
7334 emitter.removeEventListener(type, f, false); 8209 emitter.removeEventListener(type, f, false);
7335 else if (emitter.detachEvent) 8210 else if (emitter.detachEvent)
7336 emitter.detachEvent("on" + type, f); 8211 emitter.detachEvent("on" + type, f);
7337 else { 8212 else {
7338 var arr = emitter._handlers && emitter._handlers[type]; 8213 var handlers = getHandlers(emitter, type, false)
7339 if (!arr) return; 8214 for (var i = 0; i < handlers.length; ++i)
7340 for (var i = 0; i < arr.length; ++i) 8215 if (handlers[i] == f) { handlers.splice(i, 1); break; }
7341 if (arr[i] == f) { arr.splice(i, 1); break; }
7342 } 8216 }
7343 }; 8217 };
7344 8218
7345 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) { 8219 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
7346 var arr = emitter._handlers && emitter._handlers[type]; 8220 var handlers = getHandlers(emitter, type, true)
7347 if (!arr) return; 8221 if (!handlers.length) return;
7348 var args = Array.prototype.slice.call(arguments, 2); 8222 var args = Array.prototype.slice.call(arguments, 2);
7349 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); 8223 for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args);
7350 }; 8224 };
7351 8225
7352 var orphanDelayedCallbacks = null; 8226 var orphanDelayedCallbacks = null;
7353 8227
7354 // Often, we want to signal events at a point where we are in the 8228 // Often, we want to signal events at a point where we are in the
7355 // middle of some work, but don't want the handler to start calling 8229 // middle of some work, but don't want the handler to start calling
7356 // other methods on the editor, which might be in an inconsistent 8230 // other methods on the editor, which might be in an inconsistent
7357 // state or simply not expect any other events to happen. 8231 // state or simply not expect any other events to happen.
7358 // signalLater looks whether there are any handlers, and schedules 8232 // signalLater looks whether there are any handlers, and schedules
7359 // them to be executed when the last operation ends, or, if no 8233 // them to be executed when the last operation ends, or, if no
7360 // operation is active, when a timeout fires. 8234 // operation is active, when a timeout fires.
7361 function signalLater(emitter, type /*, values...*/) { 8235 function signalLater(emitter, type /*, values...*/) {
7362 var arr = emitter._handlers && emitter._handlers[type]; 8236 var arr = getHandlers(emitter, type, false)
7363 if (!arr) return; 8237 if (!arr.length) return;
7364 var args = Array.prototype.slice.call(arguments, 2), list; 8238 var args = Array.prototype.slice.call(arguments, 2), list;
7365 if (operationGroup) { 8239 if (operationGroup) {
7366 list = operationGroup.delayedCallbacks; 8240 list = operationGroup.delayedCallbacks;
7367 } else if (orphanDelayedCallbacks) { 8241 } else if (orphanDelayedCallbacks) {
7368 list = orphanDelayedCallbacks; 8242 list = orphanDelayedCallbacks;
7369 } else { 8243 } else {
7370 list = orphanDelayedCallbacks = []; 8244 list = orphanDelayedCallbacks = [];
7371 setTimeout(fireOrphanDelayed, 0); 8245 setTimeout(fireOrphanDelayed, 0);
7372 } 8246 }
7373 function bnd(f) {return function(){f.apply(null, args);};}; 8247 function bnd(f) {return function(){f.apply(null, args);};};
(...skipping 19 matching lines...) Expand all
7393 8267
7394 function signalCursorActivity(cm) { 8268 function signalCursorActivity(cm) {
7395 var arr = cm._handlers && cm._handlers.cursorActivity; 8269 var arr = cm._handlers && cm._handlers.cursorActivity;
7396 if (!arr) return; 8270 if (!arr) return;
7397 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandler s = []); 8271 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandler s = []);
7398 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) 8272 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
7399 set.push(arr[i]); 8273 set.push(arr[i]);
7400 } 8274 }
7401 8275
7402 function hasHandler(emitter, type) { 8276 function hasHandler(emitter, type) {
7403 var arr = emitter._handlers && emitter._handlers[type]; 8277 return getHandlers(emitter, type).length > 0
7404 return arr && arr.length > 0;
7405 } 8278 }
7406 8279
7407 // Add on and off methods to a constructor's prototype, to make 8280 // Add on and off methods to a constructor's prototype, to make
7408 // registering events on such objects more convenient. 8281 // registering events on such objects more convenient.
7409 function eventMixin(ctor) { 8282 function eventMixin(ctor) {
7410 ctor.prototype.on = function(type, f) {on(this, type, f);}; 8283 ctor.prototype.on = function(type, f) {on(this, type, f);};
7411 ctor.prototype.off = function(type, f) {off(this, type, f);}; 8284 ctor.prototype.off = function(type, f) {off(this, type, f);};
7412 } 8285 }
7413 8286
7414 // MISC UTILITIES 8287 // MISC UTILITIES
(...skipping 26 matching lines...) Expand all
7441 if (nextTab < 0 || nextTab >= end) 8314 if (nextTab < 0 || nextTab >= end)
7442 return n + (end - i); 8315 return n + (end - i);
7443 n += nextTab - i; 8316 n += nextTab - i;
7444 n += tabSize - (n % tabSize); 8317 n += tabSize - (n % tabSize);
7445 i = nextTab + 1; 8318 i = nextTab + 1;
7446 } 8319 }
7447 }; 8320 };
7448 8321
7449 // The inverse of countColumn -- find the offset that corresponds to 8322 // The inverse of countColumn -- find the offset that corresponds to
7450 // a particular column. 8323 // a particular column.
7451 function findColumn(string, goal, tabSize) { 8324 var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) {
7452 for (var pos = 0, col = 0;;) { 8325 for (var pos = 0, col = 0;;) {
7453 var nextTab = string.indexOf("\t", pos); 8326 var nextTab = string.indexOf("\t", pos);
7454 if (nextTab == -1) nextTab = string.length; 8327 if (nextTab == -1) nextTab = string.length;
7455 var skipped = nextTab - pos; 8328 var skipped = nextTab - pos;
7456 if (nextTab == string.length || col + skipped >= goal) 8329 if (nextTab == string.length || col + skipped >= goal)
7457 return pos + Math.min(skipped, goal - col); 8330 return pos + Math.min(skipped, goal - col);
7458 col += nextTab - pos; 8331 col += nextTab - pos;
7459 col += tabSize - (col % tabSize); 8332 col += tabSize - (col % tabSize);
7460 pos = nextTab + 1; 8333 pos = nextTab + 1;
7461 if (col >= goal) return pos; 8334 if (col >= goal) return pos;
(...skipping 19 matching lines...) Expand all
7481 for (var i = 0; i < array.length; ++i) 8354 for (var i = 0; i < array.length; ++i)
7482 if (array[i] == elt) return i; 8355 if (array[i] == elt) return i;
7483 return -1; 8356 return -1;
7484 } 8357 }
7485 function map(array, f) { 8358 function map(array, f) {
7486 var out = []; 8359 var out = [];
7487 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); 8360 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
7488 return out; 8361 return out;
7489 } 8362 }
7490 8363
8364 function nothing() {}
8365
7491 function createObj(base, props) { 8366 function createObj(base, props) {
7492 var inst; 8367 var inst;
7493 if (Object.create) { 8368 if (Object.create) {
7494 inst = Object.create(base); 8369 inst = Object.create(base);
7495 } else { 8370 } else {
7496 var ctor = function() {}; 8371 nothing.prototype = base;
7497 ctor.prototype = base; 8372 inst = new nothing();
7498 inst = new ctor();
7499 } 8373 }
7500 if (props) copyObj(props, inst); 8374 if (props) copyObj(props, inst);
7501 return inst; 8375 return inst;
7502 }; 8376 };
7503 8377
7504 function copyObj(obj, target, overwrite) { 8378 function copyObj(obj, target, overwrite) {
7505 if (!target) target = {}; 8379 if (!target) target = {};
7506 for (var prop in obj) 8380 for (var prop in obj)
7507 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProp erty(prop))) 8381 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProp erty(prop)))
7508 target[prop] = obj[prop]; 8382 target[prop] = obj[prop];
7509 return target; 8383 return target;
7510 } 8384 }
7511 8385
7512 function bind(f) { 8386 function bind(f) {
7513 var args = Array.prototype.slice.call(arguments, 1); 8387 var args = Array.prototype.slice.call(arguments, 1);
7514 return function(){return f.apply(null, args);}; 8388 return function(){return f.apply(null, args);};
7515 } 8389 }
7516 8390
7517 var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u30 9f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; 8391 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u304 0-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
7518 var isWordCharBasic = CodeMirror.isWordChar = function(ch) { 8392 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
7519 return /\w/.test(ch) || ch > "\x80" && 8393 return /\w/.test(ch) || ch > "\x80" &&
7520 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(c h)); 8394 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(c h));
7521 }; 8395 };
7522 function isWordChar(ch, helper) { 8396 function isWordChar(ch, helper) {
7523 if (!helper) return isWordCharBasic(ch); 8397 if (!helper) return isWordCharBasic(ch);
7524 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true; 8398 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
7525 return helper.test(ch); 8399 return helper.test(ch);
7526 } 8400 }
7527 8401
(...skipping 15 matching lines...) Expand all
7543 function elt(tag, content, className, style) { 8417 function elt(tag, content, className, style) {
7544 var e = document.createElement(tag); 8418 var e = document.createElement(tag);
7545 if (className) e.className = className; 8419 if (className) e.className = className;
7546 if (style) e.style.cssText = style; 8420 if (style) e.style.cssText = style;
7547 if (typeof content == "string") e.appendChild(document.createTextNode(conten t)); 8421 if (typeof content == "string") e.appendChild(document.createTextNode(conten t));
7548 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(con tent[i]); 8422 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(con tent[i]);
7549 return e; 8423 return e;
7550 } 8424 }
7551 8425
7552 var range; 8426 var range;
7553 if (document.createRange) range = function(node, start, end) { 8427 if (document.createRange) range = function(node, start, end, endNode) {
7554 var r = document.createRange(); 8428 var r = document.createRange();
7555 r.setEnd(node, end); 8429 r.setEnd(endNode || node, end);
7556 r.setStart(node, start); 8430 r.setStart(node, start);
7557 return r; 8431 return r;
7558 }; 8432 };
7559 else range = function(node, start, end) { 8433 else range = function(node, start, end) {
7560 var r = document.body.createTextRange(); 8434 var r = document.body.createTextRange();
7561 try { r.moveToElementText(node.parentNode); } 8435 try { r.moveToElementText(node.parentNode); }
7562 catch(e) { return r; } 8436 catch(e) { return r; }
7563 r.collapse(true); 8437 r.collapse(true);
7564 r.moveEnd("character", end); 8438 r.moveEnd("character", end);
7565 r.moveStart("character", start); 8439 r.moveStart("character", start);
7566 return r; 8440 return r;
7567 }; 8441 };
7568 8442
7569 function removeChildren(e) { 8443 function removeChildren(e) {
7570 for (var count = e.childNodes.length; count > 0; --count) 8444 for (var count = e.childNodes.length; count > 0; --count)
7571 e.removeChild(e.firstChild); 8445 e.removeChild(e.firstChild);
7572 return e; 8446 return e;
7573 } 8447 }
7574 8448
7575 function removeChildrenAndAdd(parent, e) { 8449 function removeChildrenAndAdd(parent, e) {
7576 return removeChildren(parent).appendChild(e); 8450 return removeChildren(parent).appendChild(e);
7577 } 8451 }
7578 8452
7579 function contains(parent, child) { 8453 var contains = CodeMirror.contains = function(parent, child) {
8454 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
8455 child = child.parentNode;
7580 if (parent.contains) 8456 if (parent.contains)
7581 return parent.contains(child); 8457 return parent.contains(child);
7582 while (child = child.parentNode) 8458 do {
8459 if (child.nodeType == 11) child = child.host;
7583 if (child == parent) return true; 8460 if (child == parent) return true;
8461 } while (child = child.parentNode);
8462 };
8463
8464 function activeElt() {
8465 var activeElement = document.activeElement;
8466 while (activeElement && activeElement.root && activeElement.root.activeEleme nt)
8467 activeElement = activeElement.root.activeElement;
8468 return activeElement;
7584 } 8469 }
7585
7586 function activeElt() { return document.activeElement; }
7587 // Older versions of IE throws unspecified error when touching 8470 // Older versions of IE throws unspecified error when touching
7588 // document.activeElement in some cases (during loading, in iframe) 8471 // document.activeElement in some cases (during loading, in iframe)
7589 if (ie && ie_version < 11) activeElt = function() { 8472 if (ie && ie_version < 11) activeElt = function() {
7590 try { return document.activeElement; } 8473 try { return document.activeElement; }
7591 catch(e) { return document.body; } 8474 catch(e) { return document.body; }
7592 }; 8475 };
7593 8476
7594 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") ; } 8477 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") ; }
7595 var rmClass = CodeMirror.rmClass = function(node, cls) { 8478 var rmClass = CodeMirror.rmClass = function(node, cls) {
7596 var current = node.className; 8479 var current = node.className;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
7659 }(); 8542 }();
7660 8543
7661 var zwspSupported; 8544 var zwspSupported;
7662 function zeroWidthElement(measure) { 8545 function zeroWidthElement(measure) {
7663 if (zwspSupported == null) { 8546 if (zwspSupported == null) {
7664 var test = elt("span", "\u200b"); 8547 var test = elt("span", "\u200b");
7665 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode(" x")])); 8548 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode(" x")]));
7666 if (measure.firstChild.offsetHeight != 0) 8549 if (measure.firstChild.offsetHeight != 0)
7667 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie & & ie_version < 8); 8550 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie & & ie_version < 8);
7668 } 8551 }
7669 if (zwspSupported) return elt("span", "\u200b"); 8552 var node = zwspSupported ? elt("span", "\u200b") :
7670 else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); 8553 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-rig ht: -1px");
8554 node.setAttribute("cm-text", "");
8555 return node;
7671 } 8556 }
7672 8557
7673 // Feature-detect IE's crummy client rect reporting for bidi text 8558 // Feature-detect IE's crummy client rect reporting for bidi text
7674 var badBidiRects; 8559 var badBidiRects;
7675 function hasBadBidiRects(measure) { 8560 function hasBadBidiRects(measure) {
7676 if (badBidiRects != null) return badBidiRects; 8561 if (badBidiRects != null) return badBidiRects;
7677 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) ; 8562 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) ;
7678 var r0 = range(txt, 0, 1).getBoundingClientRect(); 8563 var r0 = range(txt, 0, 1).getBoundingClientRect();
8564 var r1 = range(txt, 1, 2).getBoundingClientRect();
8565 removeChildren(measure);
7679 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780) 8566 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
7680 var r1 = range(txt, 1, 2).getBoundingClientRect();
7681 return badBidiRects = (r1.right - r0.right < 3); 8567 return badBidiRects = (r1.right - r0.right < 3);
7682 } 8568 }
7683 8569
7684 // See if "".split is the broken IE version, if so, provide an 8570 // See if "".split is the broken IE version, if so, provide an
7685 // alternative way to split lines. 8571 // alternative way to split lines.
7686 var splitLines = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? fun ction(string) { 8572 var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
7687 var pos = 0, result = [], l = string.length; 8573 var pos = 0, result = [], l = string.length;
7688 while (pos <= l) { 8574 while (pos <= l) {
7689 var nl = string.indexOf("\n", pos); 8575 var nl = string.indexOf("\n", pos);
7690 if (nl == -1) nl = string.length; 8576 if (nl == -1) nl = string.length;
7691 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); 8577 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
7692 var rt = line.indexOf("\r"); 8578 var rt = line.indexOf("\r");
7693 if (rt != -1) { 8579 if (rt != -1) {
7694 result.push(line.slice(0, rt)); 8580 result.push(line.slice(0, rt));
7695 pos += rt + 1; 8581 pos += rt + 1;
7696 } else { 8582 } else {
(...skipping 25 matching lines...) Expand all
7722 function hasBadZoomedRects(measure) { 8608 function hasBadZoomedRects(measure) {
7723 if (badZoomedRects != null) return badZoomedRects; 8609 if (badZoomedRects != null) return badZoomedRects;
7724 var node = removeChildrenAndAdd(measure, elt("span", "x")); 8610 var node = removeChildrenAndAdd(measure, elt("span", "x"));
7725 var normal = node.getBoundingClientRect(); 8611 var normal = node.getBoundingClientRect();
7726 var fromRange = range(node, 0, 1).getBoundingClientRect(); 8612 var fromRange = range(node, 0, 1).getBoundingClientRect();
7727 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1; 8613 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
7728 } 8614 }
7729 8615
7730 // KEY NAMES 8616 // KEY NAMES
7731 8617
7732 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift" , 17: "Ctrl", 18: "Alt", 8618 var keyNames = CodeMirror.keyNames = {
7733 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "Page Up", 34: "PageDown", 35: "End", 8619 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
7734 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 8620 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "Page Down", 35: "End",
7735 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod ", 107: "=", 109: "-", 127: "Delete", 8621 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
7736 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 19 1: "/", 192: "`", 219: "[", 220: "\\", 8622 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
7737 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 8623 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
7738 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown ", 63302: "Insert"}; 8624 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: " `", 219: "[", 220: "\\",
7739 CodeMirror.keyNames = keyNames; 8625 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right ", 63272: "Delete",
8626 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Ins ert"
8627 };
7740 (function() { 8628 (function() {
7741 // Number keys 8629 // Number keys
7742 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) ; 8630 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) ;
7743 // Alphabetic keys 8631 // Alphabetic keys
7744 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); 8632 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
7745 // Function keys 8633 // Function keys
7746 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; 8634 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
7747 })(); 8635 })();
7748 8636
7749 // BIDI HELPERS 8637 // BIDI HELPERS
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after
8023 } 8911 }
8024 } 8912 }
8025 if (order[0].level == 1 && (m = str.match(/^\s+/))) { 8913 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
8026 order[0].from = m[0].length; 8914 order[0].from = m[0].length;
8027 order.unshift(new BidiSpan(0, 0, m[0].length)); 8915 order.unshift(new BidiSpan(0, 0, m[0].length));
8028 } 8916 }
8029 if (lst(order).level == 1 && (m = str.match(/\s+$/))) { 8917 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
8030 lst(order).to -= m[0].length; 8918 lst(order).to -= m[0].length;
8031 order.push(new BidiSpan(0, len - m[0].length, len)); 8919 order.push(new BidiSpan(0, len - m[0].length, len));
8032 } 8920 }
8921 if (order[0].level == 2)
8922 order.unshift(new BidiSpan(1, order[0].to, order[0].to));
8033 if (order[0].level != lst(order).level) 8923 if (order[0].level != lst(order).level)
8034 order.push(new BidiSpan(order[0].level, len, len)); 8924 order.push(new BidiSpan(order[0].level, len, len));
8035 8925
8036 return order; 8926 return order;
8037 }; 8927 };
8038 })(); 8928 })();
8039 8929
8040 // THE END 8930 // THE END
8041 8931
8042 CodeMirror.version = "4.12.0"; 8932 CodeMirror.version = "5.17.1";
8043 8933
8044 return CodeMirror; 8934 return CodeMirror;
8045 }); 8935 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698