3 * Copyright Next Step Webs, Inc.
4 * @link https://github.com/NextStepWebs/simplemde-markdown-editor
7 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SimpleMDE = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
10 exports.toByteArray = toByteArray
11 exports.fromByteArray = fromByteArray
15 var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
18 var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
19 for (var i = 0, len = code.length; i < len; ++i) {
21 revLookup[code.charCodeAt(i)] = i
24 revLookup['-'.charCodeAt(0)] = 62
25 revLookup['_'.charCodeAt(0)] = 63
30 function toByteArray (b64) {
31 var i, j, l, tmp, placeHolders, arr
35 throw new Error('Invalid string. Length must be a multiple of 4')
38 // the number of equal signs (place holders)
39 // if there are two placeholders, than the two characters before it
41 // if there is only one, then the three characters before it represent 2 bytes
42 // this is just a cheap hack to not do indexOf twice
43 placeHolders = b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
45 // base64 is 4/3 + up to two characters of the original data
46 arr = new Arr(len * 3 / 4 - placeHolders)
48 // if there are placeholders, only get up to the last complete 4 chars
49 l = placeHolders > 0 ? len - 4 : len
53 for (i = 0, j = 0; i < l; i += 4, j += 3) {
54 tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
55 arr[L++] = (tmp >> 16) & 0xFF
56 arr[L++] = (tmp >> 8) & 0xFF
60 if (placeHolders === 2) {
61 tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
63 } else if (placeHolders === 1) {
64 tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
65 arr[L++] = (tmp >> 8) & 0xFF
72 function tripletToBase64 (num) {
73 return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
76 function encodeChunk (uint8, start, end) {
79 for (var i = start; i < end; i += 3) {
80 tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
81 output.push(tripletToBase64(tmp))
83 return output.join('')
86 function fromByteArray (uint8) {
88 var len = uint8.length
89 var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
92 var maxChunkLength = 16383 // must be multiple of 3
94 // go through the array every three bytes, we'll deal with trailing stuff later
95 for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
96 parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
99 // pad the end with zeros, but make sure to not forget the extra bytes
100 if (extraBytes === 1) {
102 output += lookup[tmp >> 2]
103 output += lookup[(tmp << 4) & 0x3F]
105 } else if (extraBytes === 2) {
106 tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
107 output += lookup[tmp >> 10]
108 output += lookup[(tmp >> 4) & 0x3F]
109 output += lookup[(tmp << 2) & 0x3F]
115 return parts.join('')
118 },{}],2:[function(require,module,exports){
120 },{}],3:[function(require,module,exports){
123 * The buffer module from node.js, for the browser.
125 * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
128 /* eslint-disable no-proto */
132 var base64 = require('base64-js')
133 var ieee754 = require('ieee754')
134 var isArray = require('isarray')
136 exports.Buffer = Buffer
137 exports.SlowBuffer = SlowBuffer
138 exports.INSPECT_MAX_BYTES = 50
141 * If `Buffer.TYPED_ARRAY_SUPPORT`:
142 * === true Use Uint8Array implementation (fastest)
143 * === false Use Object implementation (most compatible, even IE6)
145 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
146 * Opera 11.6+, iOS 4.2+.
148 * Due to various browser bugs, sometimes the Object implementation will be used even
149 * when the browser supports typed arrays.
153 * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
154 * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
156 * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
158 * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
159 * incorrect length in some situations.
161 * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
162 * get the Object implementation, which is slower but behaves correctly.
164 Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
165 ? global.TYPED_ARRAY_SUPPORT
166 : typedArraySupport()
169 * Export kMaxLength after typed array support is determined.
171 exports.kMaxLength = kMaxLength()
173 function typedArraySupport () {
175 var arr = new Uint8Array(1)
176 arr.foo = function () { return 42 }
177 return arr.foo() === 42 && // typed array instances can be augmented
178 typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
179 arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
185 function kMaxLength () {
186 return Buffer.TYPED_ARRAY_SUPPORT
191 function createBuffer (that, length) {
192 if (kMaxLength() < length) {
193 throw new RangeError('Invalid typed array length')
195 if (Buffer.TYPED_ARRAY_SUPPORT) {
196 // Return an augmented `Uint8Array` instance, for best performance
197 that = new Uint8Array(length)
198 that.__proto__ = Buffer.prototype
200 // Fallback: Return an object instance of the Buffer class
202 that = new Buffer(length)
211 * The Buffer constructor returns instances of `Uint8Array` that have their
212 * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
213 * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
214 * and the `Uint8Array` methods. Square bracket notation works as expected -- it
215 * returns a single octet.
217 * The `Uint8Array` prototype remains unmodified.
220 function Buffer (arg, encodingOrOffset, length) {
221 if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
222 return new Buffer(arg, encodingOrOffset, length)
226 if (typeof arg === 'number') {
227 if (typeof encodingOrOffset === 'string') {
229 'If encoding is specified then the first argument must be a string'
232 return allocUnsafe(this, arg)
234 return from(this, arg, encodingOrOffset, length)
237 Buffer.poolSize = 8192 // not used by this implementation
239 // TODO: Legacy, not needed anymore. Remove in next major version.
240 Buffer._augment = function (arr) {
241 arr.__proto__ = Buffer.prototype
245 function from (that, value, encodingOrOffset, length) {
246 if (typeof value === 'number') {
247 throw new TypeError('"value" argument must not be a number')
250 if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
251 return fromArrayBuffer(that, value, encodingOrOffset, length)
254 if (typeof value === 'string') {
255 return fromString(that, value, encodingOrOffset)
258 return fromObject(that, value)
262 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
263 * if value is a number.
264 * Buffer.from(str[, encoding])
266 * Buffer.from(buffer)
267 * Buffer.from(arrayBuffer[, byteOffset[, length]])
269 Buffer.from = function (value, encodingOrOffset, length) {
270 return from(null, value, encodingOrOffset, length)
273 if (Buffer.TYPED_ARRAY_SUPPORT) {
274 Buffer.prototype.__proto__ = Uint8Array.prototype
275 Buffer.__proto__ = Uint8Array
276 if (typeof Symbol !== 'undefined' && Symbol.species &&
277 Buffer[Symbol.species] === Buffer) {
278 // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
279 Object.defineProperty(Buffer, Symbol.species, {
286 function assertSize (size) {
287 if (typeof size !== 'number') {
288 throw new TypeError('"size" argument must be a number')
292 function alloc (that, size, fill, encoding) {
295 return createBuffer(that, size)
297 if (fill !== undefined) {
298 // Only pay attention to encoding if it's a string. This
299 // prevents accidentally sending in a number that would
300 // be interpretted as a start offset.
301 return typeof encoding === 'string'
302 ? createBuffer(that, size).fill(fill, encoding)
303 : createBuffer(that, size).fill(fill)
305 return createBuffer(that, size)
309 * Creates a new filled Buffer instance.
310 * alloc(size[, fill[, encoding]])
312 Buffer.alloc = function (size, fill, encoding) {
313 return alloc(null, size, fill, encoding)
316 function allocUnsafe (that, size) {
318 that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
319 if (!Buffer.TYPED_ARRAY_SUPPORT) {
320 for (var i = 0; i < size; i++) {
328 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
330 Buffer.allocUnsafe = function (size) {
331 return allocUnsafe(null, size)
334 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
336 Buffer.allocUnsafeSlow = function (size) {
337 return allocUnsafe(null, size)
340 function fromString (that, string, encoding) {
341 if (typeof encoding !== 'string' || encoding === '') {
345 if (!Buffer.isEncoding(encoding)) {
346 throw new TypeError('"encoding" must be a valid string encoding')
349 var length = byteLength(string, encoding) | 0
350 that = createBuffer(that, length)
352 that.write(string, encoding)
356 function fromArrayLike (that, array) {
357 var length = checked(array.length) | 0
358 that = createBuffer(that, length)
359 for (var i = 0; i < length; i += 1) {
360 that[i] = array[i] & 255
365 function fromArrayBuffer (that, array, byteOffset, length) {
366 array.byteLength // this throws if `array` is not a valid ArrayBuffer
368 if (byteOffset < 0 || array.byteLength < byteOffset) {
369 throw new RangeError('\'offset\' is out of bounds')
372 if (array.byteLength < byteOffset + (length || 0)) {
373 throw new RangeError('\'length\' is out of bounds')
376 if (length === undefined) {
377 array = new Uint8Array(array, byteOffset)
379 array = new Uint8Array(array, byteOffset, length)
382 if (Buffer.TYPED_ARRAY_SUPPORT) {
383 // Return an augmented `Uint8Array` instance, for best performance
385 that.__proto__ = Buffer.prototype
387 // Fallback: Return an object instance of the Buffer class
388 that = fromArrayLike(that, array)
393 function fromObject (that, obj) {
394 if (Buffer.isBuffer(obj)) {
395 var len = checked(obj.length) | 0
396 that = createBuffer(that, len)
398 if (that.length === 0) {
402 obj.copy(that, 0, 0, len)
407 if ((typeof ArrayBuffer !== 'undefined' &&
408 obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
409 if (typeof obj.length !== 'number' || isnan(obj.length)) {
410 return createBuffer(that, 0)
412 return fromArrayLike(that, obj)
415 if (obj.type === 'Buffer' && isArray(obj.data)) {
416 return fromArrayLike(that, obj.data)
420 throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
423 function checked (length) {
424 // Note: cannot use `length < kMaxLength` here because that fails when
425 // length is NaN (which is otherwise coerced to zero.)
426 if (length >= kMaxLength()) {
427 throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
428 'size: 0x' + kMaxLength().toString(16) + ' bytes')
433 function SlowBuffer (length) {
434 if (+length != length) { // eslint-disable-line eqeqeq
437 return Buffer.alloc(+length)
440 Buffer.isBuffer = function isBuffer (b) {
441 return !!(b != null && b._isBuffer)
444 Buffer.compare = function compare (a, b) {
445 if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
446 throw new TypeError('Arguments must be Buffers')
449 if (a === b) return 0
454 for (var i = 0, len = Math.min(x, y); i < len; ++i) {
467 Buffer.isEncoding = function isEncoding (encoding) {
468 switch (String(encoding).toLowerCase()) {
486 Buffer.concat = function concat (list, length) {
487 if (!isArray(list)) {
488 throw new TypeError('"list" argument must be an Array of Buffers')
491 if (list.length === 0) {
492 return Buffer.alloc(0)
496 if (length === undefined) {
498 for (i = 0; i < list.length; i++) {
499 length += list[i].length
503 var buffer = Buffer.allocUnsafe(length)
505 for (i = 0; i < list.length; i++) {
507 if (!Buffer.isBuffer(buf)) {
508 throw new TypeError('"list" argument must be an Array of Buffers')
510 buf.copy(buffer, pos)
516 function byteLength (string, encoding) {
517 if (Buffer.isBuffer(string)) {
520 if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
521 (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
522 return string.byteLength
524 if (typeof string !== 'string') {
528 var len = string.length
529 if (len === 0) return 0
531 // Use a for loop to avoid recursion
532 var loweredCase = false
544 return utf8ToBytes(string).length
553 return base64ToBytes(string).length
555 if (loweredCase) return utf8ToBytes(string).length // assume utf8
556 encoding = ('' + encoding).toLowerCase()
561 Buffer.byteLength = byteLength
563 function slowToString (encoding, start, end) {
564 var loweredCase = false
566 // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
567 // property of a typed array.
569 // This behaves neither like String nor Uint8Array in that we set start/end
570 // to their upper/lower bounds if the value passed is out of range.
571 // undefined is handled specially as per ECMA-262 6th Edition,
572 // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
573 if (start === undefined || start < 0) {
576 // Return early if start > this.length. Done here to prevent potential uint32
577 // coercion fail below.
578 if (start > this.length) {
582 if (end === undefined || end > this.length) {
590 // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
598 if (!encoding) encoding = 'utf8'
603 return hexSlice(this, start, end)
607 return utf8Slice(this, start, end)
610 return asciiSlice(this, start, end)
613 return binarySlice(this, start, end)
616 return base64Slice(this, start, end)
622 return utf16leSlice(this, start, end)
625 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
626 encoding = (encoding + '').toLowerCase()
632 // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
634 Buffer.prototype._isBuffer = true
636 function swap (b, n, m) {
642 Buffer.prototype.swap16 = function swap16 () {
643 var len = this.length
645 throw new RangeError('Buffer size must be a multiple of 16-bits')
647 for (var i = 0; i < len; i += 2) {
653 Buffer.prototype.swap32 = function swap32 () {
654 var len = this.length
656 throw new RangeError('Buffer size must be a multiple of 32-bits')
658 for (var i = 0; i < len; i += 4) {
660 swap(this, i + 1, i + 2)
665 Buffer.prototype.toString = function toString () {
666 var length = this.length | 0
667 if (length === 0) return ''
668 if (arguments.length === 0) return utf8Slice(this, 0, length)
669 return slowToString.apply(this, arguments)
672 Buffer.prototype.equals = function equals (b) {
673 if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
674 if (this === b) return true
675 return Buffer.compare(this, b) === 0
678 Buffer.prototype.inspect = function inspect () {
680 var max = exports.INSPECT_MAX_BYTES
681 if (this.length > 0) {
682 str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
683 if (this.length > max) str += ' ... '
685 return '<Buffer ' + str + '>'
688 Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
689 if (!Buffer.isBuffer(target)) {
690 throw new TypeError('Argument must be a Buffer')
693 if (start === undefined) {
696 if (end === undefined) {
697 end = target ? target.length : 0
699 if (thisStart === undefined) {
702 if (thisEnd === undefined) {
703 thisEnd = this.length
706 if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
707 throw new RangeError('out of range index')
710 if (thisStart >= thisEnd && start >= end) {
713 if (thisStart >= thisEnd) {
725 if (this === target) return 0
727 var x = thisEnd - thisStart
729 var len = Math.min(x, y)
731 var thisCopy = this.slice(thisStart, thisEnd)
732 var targetCopy = target.slice(start, end)
734 for (var i = 0; i < len; ++i) {
735 if (thisCopy[i] !== targetCopy[i]) {
747 function arrayIndexOf (arr, val, byteOffset, encoding) {
749 var arrLength = arr.length
750 var valLength = val.length
752 if (encoding !== undefined) {
753 encoding = String(encoding).toLowerCase()
754 if (encoding === 'ucs2' || encoding === 'ucs-2' ||
755 encoding === 'utf16le' || encoding === 'utf-16le') {
756 if (arr.length < 2 || val.length < 2) {
766 function read (buf, i) {
767 if (indexSize === 1) {
770 return buf.readUInt16BE(i * indexSize)
775 for (var i = 0; byteOffset + i < arrLength; i++) {
776 if (read(arr, byteOffset + i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
777 if (foundIndex === -1) foundIndex = i
778 if (i - foundIndex + 1 === valLength) return (byteOffset + foundIndex) * indexSize
780 if (foundIndex !== -1) i -= i - foundIndex
787 Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
788 if (typeof byteOffset === 'string') {
789 encoding = byteOffset
791 } else if (byteOffset > 0x7fffffff) {
792 byteOffset = 0x7fffffff
793 } else if (byteOffset < -0x80000000) {
794 byteOffset = -0x80000000
798 if (this.length === 0) return -1
799 if (byteOffset >= this.length) return -1
801 // Negative offsets start from the end of the buffer
802 if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0)
804 if (typeof val === 'string') {
805 val = Buffer.from(val, encoding)
808 if (Buffer.isBuffer(val)) {
809 // special case: looking for empty string/buffer always fails
810 if (val.length === 0) {
813 return arrayIndexOf(this, val, byteOffset, encoding)
815 if (typeof val === 'number') {
816 if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') {
817 return Uint8Array.prototype.indexOf.call(this, val, byteOffset)
819 return arrayIndexOf(this, [ val ], byteOffset, encoding)
822 throw new TypeError('val must be string, number or Buffer')
825 Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
826 return this.indexOf(val, byteOffset, encoding) !== -1
829 function hexWrite (buf, string, offset, length) {
830 offset = Number(offset) || 0
831 var remaining = buf.length - offset
835 length = Number(length)
836 if (length > remaining) {
841 // must be an even number of digits
842 var strLen = string.length
843 if (strLen % 2 !== 0) throw new Error('Invalid hex string')
845 if (length > strLen / 2) {
848 for (var i = 0; i < length; i++) {
849 var parsed = parseInt(string.substr(i * 2, 2), 16)
850 if (isNaN(parsed)) return i
851 buf[offset + i] = parsed
856 function utf8Write (buf, string, offset, length) {
857 return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
860 function asciiWrite (buf, string, offset, length) {
861 return blitBuffer(asciiToBytes(string), buf, offset, length)
864 function binaryWrite (buf, string, offset, length) {
865 return asciiWrite(buf, string, offset, length)
868 function base64Write (buf, string, offset, length) {
869 return blitBuffer(base64ToBytes(string), buf, offset, length)
872 function ucs2Write (buf, string, offset, length) {
873 return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
876 Buffer.prototype.write = function write (string, offset, length, encoding) {
877 // Buffer#write(string)
878 if (offset === undefined) {
882 // Buffer#write(string, encoding)
883 } else if (length === undefined && typeof offset === 'string') {
887 // Buffer#write(string, offset[, length][, encoding])
888 } else if (isFinite(offset)) {
890 if (isFinite(length)) {
892 if (encoding === undefined) encoding = 'utf8'
897 // legacy write(string, encoding, offset, length) - remove in v0.13
900 'Buffer.write(string, encoding, offset[, length]) is no longer supported'
904 var remaining = this.length - offset
905 if (length === undefined || length > remaining) length = remaining
907 if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
908 throw new RangeError('Attempt to write outside buffer bounds')
911 if (!encoding) encoding = 'utf8'
913 var loweredCase = false
917 return hexWrite(this, string, offset, length)
921 return utf8Write(this, string, offset, length)
924 return asciiWrite(this, string, offset, length)
927 return binaryWrite(this, string, offset, length)
930 // Warning: maxLength not taken into account in base64Write
931 return base64Write(this, string, offset, length)
937 return ucs2Write(this, string, offset, length)
940 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
941 encoding = ('' + encoding).toLowerCase()
947 Buffer.prototype.toJSON = function toJSON () {
950 data: Array.prototype.slice.call(this._arr || this, 0)
954 function base64Slice (buf, start, end) {
955 if (start === 0 && end === buf.length) {
956 return base64.fromByteArray(buf)
958 return base64.fromByteArray(buf.slice(start, end))
962 function utf8Slice (buf, start, end) {
963 end = Math.min(buf.length, end)
968 var firstByte = buf[i]
970 var bytesPerSequence = (firstByte > 0xEF) ? 4
971 : (firstByte > 0xDF) ? 3
972 : (firstByte > 0xBF) ? 2
975 if (i + bytesPerSequence <= end) {
976 var secondByte, thirdByte, fourthByte, tempCodePoint
978 switch (bytesPerSequence) {
980 if (firstByte < 0x80) {
981 codePoint = firstByte
985 secondByte = buf[i + 1]
986 if ((secondByte & 0xC0) === 0x80) {
987 tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
988 if (tempCodePoint > 0x7F) {
989 codePoint = tempCodePoint
994 secondByte = buf[i + 1]
995 thirdByte = buf[i + 2]
996 if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
997 tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
998 if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
999 codePoint = tempCodePoint
1004 secondByte = buf[i + 1]
1005 thirdByte = buf[i + 2]
1006 fourthByte = buf[i + 3]
1007 if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
1008 tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
1009 if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
1010 codePoint = tempCodePoint
1016 if (codePoint === null) {
1017 // we did not generate a valid codePoint so insert a
1018 // replacement char (U+FFFD) and advance only 1 byte
1020 bytesPerSequence = 1
1021 } else if (codePoint > 0xFFFF) {
1022 // encode to utf16 (surrogate pair dance)
1023 codePoint -= 0x10000
1024 res.push(codePoint >>> 10 & 0x3FF | 0xD800)
1025 codePoint = 0xDC00 | codePoint & 0x3FF
1029 i += bytesPerSequence
1032 return decodeCodePointsArray(res)
1035 // Based on http://stackoverflow.com/a/22747272/680742, the browser with
1036 // the lowest limit is Chrome, with 0x10000 args.
1037 // We go 1 magnitude less, for safety
1038 var MAX_ARGUMENTS_LENGTH = 0x1000
1040 function decodeCodePointsArray (codePoints) {
1041 var len = codePoints.length
1042 if (len <= MAX_ARGUMENTS_LENGTH) {
1043 return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
1046 // Decode in chunks to avoid "call stack size exceeded".
1050 res += String.fromCharCode.apply(
1052 codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
1058 function asciiSlice (buf, start, end) {
1060 end = Math.min(buf.length, end)
1062 for (var i = start; i < end; i++) {
1063 ret += String.fromCharCode(buf[i] & 0x7F)
1068 function binarySlice (buf, start, end) {
1070 end = Math.min(buf.length, end)
1072 for (var i = start; i < end; i++) {
1073 ret += String.fromCharCode(buf[i])
1078 function hexSlice (buf, start, end) {
1079 var len = buf.length
1081 if (!start || start < 0) start = 0
1082 if (!end || end < 0 || end > len) end = len
1085 for (var i = start; i < end; i++) {
1086 out += toHex(buf[i])
1091 function utf16leSlice (buf, start, end) {
1092 var bytes = buf.slice(start, end)
1094 for (var i = 0; i < bytes.length; i += 2) {
1095 res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
1100 Buffer.prototype.slice = function slice (start, end) {
1101 var len = this.length
1103 end = end === undefined ? len : ~~end
1107 if (start < 0) start = 0
1108 } else if (start > len) {
1114 if (end < 0) end = 0
1115 } else if (end > len) {
1119 if (end < start) end = start
1122 if (Buffer.TYPED_ARRAY_SUPPORT) {
1123 newBuf = this.subarray(start, end)
1124 newBuf.__proto__ = Buffer.prototype
1126 var sliceLen = end - start
1127 newBuf = new Buffer(sliceLen, undefined)
1128 for (var i = 0; i < sliceLen; i++) {
1129 newBuf[i] = this[i + start]
1137 * Need to make sure that buffer isn't trying to write out of bounds.
1139 function checkOffset (offset, ext, length) {
1140 if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
1141 if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
1144 Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
1146 byteLength = byteLength | 0
1147 if (!noAssert) checkOffset(offset, byteLength, this.length)
1149 var val = this[offset]
1152 while (++i < byteLength && (mul *= 0x100)) {
1153 val += this[offset + i] * mul
1159 Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
1161 byteLength = byteLength | 0
1163 checkOffset(offset, byteLength, this.length)
1166 var val = this[offset + --byteLength]
1168 while (byteLength > 0 && (mul *= 0x100)) {
1169 val += this[offset + --byteLength] * mul
1175 Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
1176 if (!noAssert) checkOffset(offset, 1, this.length)
1180 Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
1181 if (!noAssert) checkOffset(offset, 2, this.length)
1182 return this[offset] | (this[offset + 1] << 8)
1185 Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
1186 if (!noAssert) checkOffset(offset, 2, this.length)
1187 return (this[offset] << 8) | this[offset + 1]
1190 Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
1191 if (!noAssert) checkOffset(offset, 4, this.length)
1193 return ((this[offset]) |
1194 (this[offset + 1] << 8) |
1195 (this[offset + 2] << 16)) +
1196 (this[offset + 3] * 0x1000000)
1199 Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
1200 if (!noAssert) checkOffset(offset, 4, this.length)
1202 return (this[offset] * 0x1000000) +
1203 ((this[offset + 1] << 16) |
1204 (this[offset + 2] << 8) |
1208 Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
1210 byteLength = byteLength | 0
1211 if (!noAssert) checkOffset(offset, byteLength, this.length)
1213 var val = this[offset]
1216 while (++i < byteLength && (mul *= 0x100)) {
1217 val += this[offset + i] * mul
1221 if (val >= mul) val -= Math.pow(2, 8 * byteLength)
1226 Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
1228 byteLength = byteLength | 0
1229 if (!noAssert) checkOffset(offset, byteLength, this.length)
1233 var val = this[offset + --i]
1234 while (i > 0 && (mul *= 0x100)) {
1235 val += this[offset + --i] * mul
1239 if (val >= mul) val -= Math.pow(2, 8 * byteLength)
1244 Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
1245 if (!noAssert) checkOffset(offset, 1, this.length)
1246 if (!(this[offset] & 0x80)) return (this[offset])
1247 return ((0xff - this[offset] + 1) * -1)
1250 Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
1251 if (!noAssert) checkOffset(offset, 2, this.length)
1252 var val = this[offset] | (this[offset + 1] << 8)
1253 return (val & 0x8000) ? val | 0xFFFF0000 : val
1256 Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
1257 if (!noAssert) checkOffset(offset, 2, this.length)
1258 var val = this[offset + 1] | (this[offset] << 8)
1259 return (val & 0x8000) ? val | 0xFFFF0000 : val
1262 Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
1263 if (!noAssert) checkOffset(offset, 4, this.length)
1265 return (this[offset]) |
1266 (this[offset + 1] << 8) |
1267 (this[offset + 2] << 16) |
1268 (this[offset + 3] << 24)
1271 Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
1272 if (!noAssert) checkOffset(offset, 4, this.length)
1274 return (this[offset] << 24) |
1275 (this[offset + 1] << 16) |
1276 (this[offset + 2] << 8) |
1280 Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
1281 if (!noAssert) checkOffset(offset, 4, this.length)
1282 return ieee754.read(this, offset, true, 23, 4)
1285 Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
1286 if (!noAssert) checkOffset(offset, 4, this.length)
1287 return ieee754.read(this, offset, false, 23, 4)
1290 Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
1291 if (!noAssert) checkOffset(offset, 8, this.length)
1292 return ieee754.read(this, offset, true, 52, 8)
1295 Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
1296 if (!noAssert) checkOffset(offset, 8, this.length)
1297 return ieee754.read(this, offset, false, 52, 8)
1300 function checkInt (buf, value, offset, ext, max, min) {
1301 if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
1302 if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
1303 if (offset + ext > buf.length) throw new RangeError('Index out of range')
1306 Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
1309 byteLength = byteLength | 0
1311 var maxBytes = Math.pow(2, 8 * byteLength) - 1
1312 checkInt(this, value, offset, byteLength, maxBytes, 0)
1317 this[offset] = value & 0xFF
1318 while (++i < byteLength && (mul *= 0x100)) {
1319 this[offset + i] = (value / mul) & 0xFF
1322 return offset + byteLength
1325 Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
1328 byteLength = byteLength | 0
1330 var maxBytes = Math.pow(2, 8 * byteLength) - 1
1331 checkInt(this, value, offset, byteLength, maxBytes, 0)
1334 var i = byteLength - 1
1336 this[offset + i] = value & 0xFF
1337 while (--i >= 0 && (mul *= 0x100)) {
1338 this[offset + i] = (value / mul) & 0xFF
1341 return offset + byteLength
1344 Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
1347 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
1348 if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
1349 this[offset] = (value & 0xff)
1353 function objectWriteUInt16 (buf, value, offset, littleEndian) {
1354 if (value < 0) value = 0xffff + value + 1
1355 for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) {
1356 buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
1357 (littleEndian ? i : 1 - i) * 8
1361 Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
1364 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
1365 if (Buffer.TYPED_ARRAY_SUPPORT) {
1366 this[offset] = (value & 0xff)
1367 this[offset + 1] = (value >>> 8)
1369 objectWriteUInt16(this, value, offset, true)
1374 Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
1377 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
1378 if (Buffer.TYPED_ARRAY_SUPPORT) {
1379 this[offset] = (value >>> 8)
1380 this[offset + 1] = (value & 0xff)
1382 objectWriteUInt16(this, value, offset, false)
1387 function objectWriteUInt32 (buf, value, offset, littleEndian) {
1388 if (value < 0) value = 0xffffffff + value + 1
1389 for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) {
1390 buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
1394 Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
1397 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
1398 if (Buffer.TYPED_ARRAY_SUPPORT) {
1399 this[offset + 3] = (value >>> 24)
1400 this[offset + 2] = (value >>> 16)
1401 this[offset + 1] = (value >>> 8)
1402 this[offset] = (value & 0xff)
1404 objectWriteUInt32(this, value, offset, true)
1409 Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
1412 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
1413 if (Buffer.TYPED_ARRAY_SUPPORT) {
1414 this[offset] = (value >>> 24)
1415 this[offset + 1] = (value >>> 16)
1416 this[offset + 2] = (value >>> 8)
1417 this[offset + 3] = (value & 0xff)
1419 objectWriteUInt32(this, value, offset, false)
1424 Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
1428 var limit = Math.pow(2, 8 * byteLength - 1)
1430 checkInt(this, value, offset, byteLength, limit - 1, -limit)
1436 this[offset] = value & 0xFF
1437 while (++i < byteLength && (mul *= 0x100)) {
1438 if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
1441 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
1444 return offset + byteLength
1447 Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
1451 var limit = Math.pow(2, 8 * byteLength - 1)
1453 checkInt(this, value, offset, byteLength, limit - 1, -limit)
1456 var i = byteLength - 1
1459 this[offset + i] = value & 0xFF
1460 while (--i >= 0 && (mul *= 0x100)) {
1461 if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
1464 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
1467 return offset + byteLength
1470 Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
1473 if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
1474 if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
1475 if (value < 0) value = 0xff + value + 1
1476 this[offset] = (value & 0xff)
1480 Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
1483 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
1484 if (Buffer.TYPED_ARRAY_SUPPORT) {
1485 this[offset] = (value & 0xff)
1486 this[offset + 1] = (value >>> 8)
1488 objectWriteUInt16(this, value, offset, true)
1493 Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
1496 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
1497 if (Buffer.TYPED_ARRAY_SUPPORT) {
1498 this[offset] = (value >>> 8)
1499 this[offset + 1] = (value & 0xff)
1501 objectWriteUInt16(this, value, offset, false)
1506 Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
1509 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
1510 if (Buffer.TYPED_ARRAY_SUPPORT) {
1511 this[offset] = (value & 0xff)
1512 this[offset + 1] = (value >>> 8)
1513 this[offset + 2] = (value >>> 16)
1514 this[offset + 3] = (value >>> 24)
1516 objectWriteUInt32(this, value, offset, true)
1521 Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
1524 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
1525 if (value < 0) value = 0xffffffff + value + 1
1526 if (Buffer.TYPED_ARRAY_SUPPORT) {
1527 this[offset] = (value >>> 24)
1528 this[offset + 1] = (value >>> 16)
1529 this[offset + 2] = (value >>> 8)
1530 this[offset + 3] = (value & 0xff)
1532 objectWriteUInt32(this, value, offset, false)
1537 function checkIEEE754 (buf, value, offset, ext, max, min) {
1538 if (offset + ext > buf.length) throw new RangeError('Index out of range')
1539 if (offset < 0) throw new RangeError('Index out of range')
1542 function writeFloat (buf, value, offset, littleEndian, noAssert) {
1544 checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
1546 ieee754.write(buf, value, offset, littleEndian, 23, 4)
1550 Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
1551 return writeFloat(this, value, offset, true, noAssert)
1554 Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
1555 return writeFloat(this, value, offset, false, noAssert)
1558 function writeDouble (buf, value, offset, littleEndian, noAssert) {
1560 checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
1562 ieee754.write(buf, value, offset, littleEndian, 52, 8)
1566 Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
1567 return writeDouble(this, value, offset, true, noAssert)
1570 Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
1571 return writeDouble(this, value, offset, false, noAssert)
1574 // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
1575 Buffer.prototype.copy = function copy (target, targetStart, start, end) {
1576 if (!start) start = 0
1577 if (!end && end !== 0) end = this.length
1578 if (targetStart >= target.length) targetStart = target.length
1579 if (!targetStart) targetStart = 0
1580 if (end > 0 && end < start) end = start
1582 // Copy 0 bytes; we're done
1583 if (end === start) return 0
1584 if (target.length === 0 || this.length === 0) return 0
1586 // Fatal error conditions
1587 if (targetStart < 0) {
1588 throw new RangeError('targetStart out of bounds')
1590 if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
1591 if (end < 0) throw new RangeError('sourceEnd out of bounds')
1594 if (end > this.length) end = this.length
1595 if (target.length - targetStart < end - start) {
1596 end = target.length - targetStart + start
1599 var len = end - start
1602 if (this === target && start < targetStart && targetStart < end) {
1603 // descending copy from end
1604 for (i = len - 1; i >= 0; i--) {
1605 target[i + targetStart] = this[i + start]
1607 } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
1608 // ascending copy from start
1609 for (i = 0; i < len; i++) {
1610 target[i + targetStart] = this[i + start]
1613 Uint8Array.prototype.set.call(
1615 this.subarray(start, start + len),
1624 // buffer.fill(number[, offset[, end]])
1625 // buffer.fill(buffer[, offset[, end]])
1626 // buffer.fill(string[, offset[, end]][, encoding])
1627 Buffer.prototype.fill = function fill (val, start, end, encoding) {
1628 // Handle string cases:
1629 if (typeof val === 'string') {
1630 if (typeof start === 'string') {
1634 } else if (typeof end === 'string') {
1638 if (val.length === 1) {
1639 var code = val.charCodeAt(0)
1644 if (encoding !== undefined && typeof encoding !== 'string') {
1645 throw new TypeError('encoding must be a string')
1647 if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
1648 throw new TypeError('Unknown encoding: ' + encoding)
1650 } else if (typeof val === 'number') {
1654 // Invalid ranges are not set to a default, so can range check early.
1655 if (start < 0 || this.length < start || this.length < end) {
1656 throw new RangeError('Out of range index')
1664 end = end === undefined ? this.length : end >>> 0
1669 if (typeof val === 'number') {
1670 for (i = start; i < end; i++) {
1674 var bytes = Buffer.isBuffer(val)
1676 : utf8ToBytes(new Buffer(val, encoding).toString())
1677 var len = bytes.length
1678 for (i = 0; i < end - start; i++) {
1679 this[i + start] = bytes[i % len]
1689 var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g
1691 function base64clean (str) {
1692 // Node strips out invalid characters like \n and \t from the string, base64-js does not
1693 str = stringtrim(str).replace(INVALID_BASE64_RE, '')
1694 // Node converts strings with length < 2 to ''
1695 if (str.length < 2) return ''
1696 // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
1697 while (str.length % 4 !== 0) {
1703 function stringtrim (str) {
1704 if (str.trim) return str.trim()
1705 return str.replace(/^\s+|\s+$/g, '')
1708 function toHex (n) {
1709 if (n < 16) return '0' + n.toString(16)
1710 return n.toString(16)
1713 function utf8ToBytes (string, units) {
1714 units = units || Infinity
1716 var length = string.length
1717 var leadSurrogate = null
1720 for (var i = 0; i < length; i++) {
1721 codePoint = string.charCodeAt(i)
1723 // is surrogate component
1724 if (codePoint > 0xD7FF && codePoint < 0xE000) {
1725 // last char was a lead
1726 if (!leadSurrogate) {
1728 if (codePoint > 0xDBFF) {
1730 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
1732 } else if (i + 1 === length) {
1734 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
1739 leadSurrogate = codePoint
1745 if (codePoint < 0xDC00) {
1746 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
1747 leadSurrogate = codePoint
1751 // valid surrogate pair
1752 codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
1753 } else if (leadSurrogate) {
1754 // valid bmp char, but last char was a lead
1755 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
1758 leadSurrogate = null
1761 if (codePoint < 0x80) {
1762 if ((units -= 1) < 0) break
1763 bytes.push(codePoint)
1764 } else if (codePoint < 0x800) {
1765 if ((units -= 2) < 0) break
1767 codePoint >> 0x6 | 0xC0,
1768 codePoint & 0x3F | 0x80
1770 } else if (codePoint < 0x10000) {
1771 if ((units -= 3) < 0) break
1773 codePoint >> 0xC | 0xE0,
1774 codePoint >> 0x6 & 0x3F | 0x80,
1775 codePoint & 0x3F | 0x80
1777 } else if (codePoint < 0x110000) {
1778 if ((units -= 4) < 0) break
1780 codePoint >> 0x12 | 0xF0,
1781 codePoint >> 0xC & 0x3F | 0x80,
1782 codePoint >> 0x6 & 0x3F | 0x80,
1783 codePoint & 0x3F | 0x80
1786 throw new Error('Invalid code point')
1793 function asciiToBytes (str) {
1795 for (var i = 0; i < str.length; i++) {
1796 // Node's code seems to be doing this and not & 0x7F..
1797 byteArray.push(str.charCodeAt(i) & 0xFF)
1802 function utf16leToBytes (str, units) {
1805 for (var i = 0; i < str.length; i++) {
1806 if ((units -= 2) < 0) break
1808 c = str.charCodeAt(i)
1818 function base64ToBytes (str) {
1819 return base64.toByteArray(base64clean(str))
1822 function blitBuffer (src, dst, offset, length) {
1823 for (var i = 0; i < length; i++) {
1824 if ((i + offset >= dst.length) || (i >= src.length)) break
1825 dst[i + offset] = src[i]
1830 function isnan (val) {
1831 return val !== val // eslint-disable-line no-self-compare
1834 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1835 },{"base64-js":1,"ieee754":15,"isarray":16}],4:[function(require,module,exports){
1836 // Use strict mode (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)
1841 var Typo = require("typo-js");
1845 function CodeMirrorSpellChecker(options) {
1847 options = options || {};
1851 if(typeof options.codeMirrorInstance !== "function" || typeof options.codeMirrorInstance.defineMode !== "function") {
1852 console.log("CodeMirror Spell Checker: You must provide an instance of CodeMirror via the option `codeMirrorInstance`");
1857 // Because some browsers don't support this functionality yet
1858 if(!String.prototype.includes) {
1859 String.prototype.includes = function() {
1861 return String.prototype.indexOf.apply(this, arguments) !== -1;
1866 // Define the new mode
1867 options.codeMirrorInstance.defineMode("spell-checker", function(config) {
1868 // Load AFF/DIC data
1869 if(!CodeMirrorSpellChecker.aff_loading) {
1870 CodeMirrorSpellChecker.aff_loading = true;
1871 var xhr_aff = new XMLHttpRequest();
1872 xhr_aff.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.aff", true);
1873 xhr_aff.onload = function() {
1874 if(xhr_aff.readyState === 4 && xhr_aff.status === 200) {
1875 CodeMirrorSpellChecker.aff_data = xhr_aff.responseText;
1876 CodeMirrorSpellChecker.num_loaded++;
1878 if(CodeMirrorSpellChecker.num_loaded == 2) {
1879 CodeMirrorSpellChecker.typo = new Typo("en_US", CodeMirrorSpellChecker.aff_data, CodeMirrorSpellChecker.dic_data, {
1888 if(!CodeMirrorSpellChecker.dic_loading) {
1889 CodeMirrorSpellChecker.dic_loading = true;
1890 var xhr_dic = new XMLHttpRequest();
1891 xhr_dic.open("GET", "https://cdn.jsdelivr.net/codemirror.spell-checker/latest/en_US.dic", true);
1892 xhr_dic.onload = function() {
1893 if(xhr_dic.readyState === 4 && xhr_dic.status === 200) {
1894 CodeMirrorSpellChecker.dic_data = xhr_dic.responseText;
1895 CodeMirrorSpellChecker.num_loaded++;
1897 if(CodeMirrorSpellChecker.num_loaded == 2) {
1898 CodeMirrorSpellChecker.typo = new Typo("en_US", CodeMirrorSpellChecker.aff_data, CodeMirrorSpellChecker.dic_data, {
1908 // Define what separates a word
1909 var rx_word = "!\"#$%&()*+,-./:;<=>?@[\\]^_`{|}~ ";
1912 // Create the overlay and such
1914 token: function(stream) {
1915 var ch = stream.peek();
1918 if(rx_word.includes(ch)) {
1923 while((ch = stream.peek()) != null && !rx_word.includes(ch)) {
1928 if(CodeMirrorSpellChecker.typo && !CodeMirrorSpellChecker.typo.check(word))
1929 return "spell-error"; // CSS class: cm-spell-error
1935 var mode = options.codeMirrorInstance.getMode(
1936 config, config.backdrop || "text/plain"
1939 return options.codeMirrorInstance.overlayMode(mode, overlay, true);
1944 // Initialize data globally to reduce memory consumption
1945 CodeMirrorSpellChecker.num_loaded = 0;
1946 CodeMirrorSpellChecker.aff_loading = false;
1947 CodeMirrorSpellChecker.dic_loading = false;
1948 CodeMirrorSpellChecker.aff_data = "";
1949 CodeMirrorSpellChecker.dic_data = "";
1950 CodeMirrorSpellChecker.typo;
1954 module.exports = CodeMirrorSpellChecker;
1955 },{"typo-js":18}],5:[function(require,module,exports){
1956 // CodeMirror, copyright (c) by Marijn Haverbeke and others
1957 // Distributed under an MIT license: http://codemirror.net/LICENSE
1960 if (typeof exports == "object" && typeof module == "object") // CommonJS
1961 mod(require("../../lib/codemirror"));
1962 else if (typeof define == "function" && define.amd) // AMD
1963 define(["../../lib/codemirror"], mod);
1964 else // Plain browser env
1966 })(function(CodeMirror) {
1969 CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
1970 if (old == CodeMirror.Init) old = false;
1971 if (!old == !val) return;
1972 if (val) setFullscreen(cm);
1976 function setFullscreen(cm) {
1977 var wrap = cm.getWrapperElement();
1978 cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
1979 width: wrap.style.width, height: wrap.style.height};
1980 wrap.style.width = "";
1981 wrap.style.height = "auto";
1982 wrap.className += " CodeMirror-fullscreen";
1983 document.documentElement.style.overflow = "hidden";
1987 function setNormal(cm) {
1988 var wrap = cm.getWrapperElement();
1989 wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
1990 document.documentElement.style.overflow = "";
1991 var info = cm.state.fullScreenRestore;
1992 wrap.style.width = info.width; wrap.style.height = info.height;
1993 window.scrollTo(info.scrollLeft, info.scrollTop);
1998 },{"../../lib/codemirror":10}],6:[function(require,module,exports){
1999 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2000 // Distributed under an MIT license: http://codemirror.net/LICENSE
2003 if (typeof exports == "object" && typeof module == "object") // CommonJS
2004 mod(require("../../lib/codemirror"));
2005 else if (typeof define == "function" && define.amd) // AMD
2006 define(["../../lib/codemirror"], mod);
2007 else // Plain browser env
2009 })(function(CodeMirror) {
2010 CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
2011 var prev = old && old != CodeMirror.Init;
2013 cm.on("blur", onBlur);
2014 cm.on("change", onChange);
2015 cm.on("swapDoc", onChange);
2017 } else if (!val && prev) {
2018 cm.off("blur", onBlur);
2019 cm.off("change", onChange);
2020 cm.off("swapDoc", onChange);
2021 clearPlaceholder(cm);
2022 var wrapper = cm.getWrapperElement();
2023 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
2026 if (val && !cm.hasFocus()) onBlur(cm);
2029 function clearPlaceholder(cm) {
2030 if (cm.state.placeholder) {
2031 cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
2032 cm.state.placeholder = null;
2035 function setPlaceholder(cm) {
2036 clearPlaceholder(cm);
2037 var elt = cm.state.placeholder = document.createElement("pre");
2038 elt.style.cssText = "height: 0; overflow: visible";
2039 elt.className = "CodeMirror-placeholder";
2040 var placeHolder = cm.getOption("placeholder")
2041 if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
2042 elt.appendChild(placeHolder)
2043 cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
2046 function onBlur(cm) {
2047 if (isEmpty(cm)) setPlaceholder(cm);
2049 function onChange(cm) {
2050 var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
2051 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
2053 if (empty) setPlaceholder(cm);
2054 else clearPlaceholder(cm);
2057 function isEmpty(cm) {
2058 return (cm.lineCount() === 1) && (cm.getLine(0) === "");
2062 },{"../../lib/codemirror":10}],7:[function(require,module,exports){
2063 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2064 // Distributed under an MIT license: http://codemirror.net/LICENSE
2067 if (typeof exports == "object" && typeof module == "object") // CommonJS
2068 mod(require("../../lib/codemirror"));
2069 else if (typeof define == "function" && define.amd) // AMD
2070 define(["../../lib/codemirror"], mod);
2071 else // Plain browser env
2073 })(function(CodeMirror) {
2076 var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))(\s*)/,
2077 emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)[.)])(\s*)$/,
2078 unorderedListRE = /[*+-]\s/;
2080 CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
2081 if (cm.getOption("disableInput")) return CodeMirror.Pass;
2082 var ranges = cm.listSelections(), replacements = [];
2083 for (var i = 0; i < ranges.length; i++) {
2084 var pos = ranges[i].head;
2085 var eolState = cm.getStateAfter(pos.line);
2086 var inList = eolState.list !== false;
2087 var inQuote = eolState.quote !== 0;
2089 var line = cm.getLine(pos.line), match = listRE.exec(line);
2090 if (!ranges[i].empty() || (!inList && !inQuote) || !match) {
2091 cm.execCommand("newlineAndIndent");
2094 if (emptyListRE.test(line)) {
2095 cm.replaceRange("", {
2096 line: pos.line, ch: 0
2098 line: pos.line, ch: pos.ch + 1
2100 replacements[i] = "\n";
2102 var indent = match[1], after = match[5];
2103 var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
2105 : (parseInt(match[3], 10) + 1) + match[4];
2107 replacements[i] = "\n" + indent + bullet + after;
2111 cm.replaceSelections(replacements);
2115 },{"../../lib/codemirror":10}],8:[function(require,module,exports){
2116 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2117 // Distributed under an MIT license: http://codemirror.net/LICENSE
2119 // Utility function that allows modes to be combined. The mode given
2120 // as the base argument takes care of most of the normal mode
2121 // functionality, but a second (typically simple) mode is used, which
2122 // can override the style of text. Both modes get to parse all of the
2123 // text, but when both assign a non-null style to a piece of code, the
2124 // overlay wins, unless the combine argument was true and not overridden,
2125 // or state.overlay.combineTokens was true, in which case the styles are
2129 if (typeof exports == "object" && typeof module == "object") // CommonJS
2130 mod(require("../../lib/codemirror"));
2131 else if (typeof define == "function" && define.amd) // AMD
2132 define(["../../lib/codemirror"], mod);
2133 else // Plain browser env
2135 })(function(CodeMirror) {
2138 CodeMirror.overlayMode = function(base, overlay, combine) {
2140 startState: function() {
2142 base: CodeMirror.startState(base),
2143 overlay: CodeMirror.startState(overlay),
2144 basePos: 0, baseCur: null,
2145 overlayPos: 0, overlayCur: null,
2149 copyState: function(state) {
2151 base: CodeMirror.copyState(base, state.base),
2152 overlay: CodeMirror.copyState(overlay, state.overlay),
2153 basePos: state.basePos, baseCur: null,
2154 overlayPos: state.overlayPos, overlayCur: null
2158 token: function(stream, state) {
2159 if (stream != state.streamSeen ||
2160 Math.min(state.basePos, state.overlayPos) < stream.start) {
2161 state.streamSeen = stream;
2162 state.basePos = state.overlayPos = stream.start;
2165 if (stream.start == state.basePos) {
2166 state.baseCur = base.token(stream, state.base);
2167 state.basePos = stream.pos;
2169 if (stream.start == state.overlayPos) {
2170 stream.pos = stream.start;
2171 state.overlayCur = overlay.token(stream, state.overlay);
2172 state.overlayPos = stream.pos;
2174 stream.pos = Math.min(state.basePos, state.overlayPos);
2176 // state.overlay.combineTokens always takes precedence over combine,
2177 // unless set to null
2178 if (state.overlayCur == null) return state.baseCur;
2179 else if (state.baseCur != null &&
2180 state.overlay.combineTokens ||
2181 combine && state.overlay.combineTokens == null)
2182 return state.baseCur + " " + state.overlayCur;
2183 else return state.overlayCur;
2186 indent: base.indent && function(state, textAfter) {
2187 return base.indent(state.base, textAfter);
2189 electricChars: base.electricChars,
2191 innerMode: function(state) { return {state: state.base, mode: base}; },
2193 blankLine: function(state) {
2194 if (base.blankLine) base.blankLine(state.base);
2195 if (overlay.blankLine) overlay.blankLine(state.overlay);
2202 },{"../../lib/codemirror":10}],9:[function(require,module,exports){
2203 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2204 // Distributed under an MIT license: http://codemirror.net/LICENSE
2206 // Because sometimes you need to mark the selected *text*.
2208 // Adds an option 'styleSelectedText' which, when enabled, gives
2209 // selected text the CSS class given as option value, or
2210 // "CodeMirror-selectedtext" when the value is not a string.
2213 if (typeof exports == "object" && typeof module == "object") // CommonJS
2214 mod(require("../../lib/codemirror"));
2215 else if (typeof define == "function" && define.amd) // AMD
2216 define(["../../lib/codemirror"], mod);
2217 else // Plain browser env
2219 })(function(CodeMirror) {
2222 CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
2223 var prev = old && old != CodeMirror.Init;
2225 cm.state.markedSelection = [];
2226 cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
2228 cm.on("cursorActivity", onCursorActivity);
2229 cm.on("change", onChange);
2230 } else if (!val && prev) {
2231 cm.off("cursorActivity", onCursorActivity);
2232 cm.off("change", onChange);
2234 cm.state.markedSelection = cm.state.markedSelectionStyle = null;
2238 function onCursorActivity(cm) {
2239 cm.operation(function() { update(cm); });
2242 function onChange(cm) {
2243 if (cm.state.markedSelection.length)
2244 cm.operation(function() { clear(cm); });
2248 var Pos = CodeMirror.Pos;
2249 var cmp = CodeMirror.cmpPos;
2251 function coverRange(cm, from, to, addAt) {
2252 if (cmp(from, to) == 0) return;
2253 var array = cm.state.markedSelection;
2254 var cls = cm.state.markedSelectionStyle;
2255 for (var line = from.line;;) {
2256 var start = line == from.line ? from : Pos(line, 0);
2257 var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
2258 var end = atEnd ? to : Pos(endLine, 0);
2259 var mark = cm.markText(start, end, {className: cls});
2260 if (addAt == null) array.push(mark);
2261 else array.splice(addAt++, 0, mark);
2267 function clear(cm) {
2268 var array = cm.state.markedSelection;
2269 for (var i = 0; i < array.length; ++i) array[i].clear();
2273 function reset(cm) {
2275 var ranges = cm.listSelections();
2276 for (var i = 0; i < ranges.length; i++)
2277 coverRange(cm, ranges[i].from(), ranges[i].to());
2280 function update(cm) {
2281 if (!cm.somethingSelected()) return clear(cm);
2282 if (cm.listSelections().length > 1) return reset(cm);
2284 var from = cm.getCursor("start"), to = cm.getCursor("end");
2286 var array = cm.state.markedSelection;
2287 if (!array.length) return coverRange(cm, from, to);
2289 var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
2290 if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE ||
2291 cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
2294 while (cmp(from, coverStart.from) > 0) {
2295 array.shift().clear();
2296 coverStart = array[0].find();
2298 if (cmp(from, coverStart.from) < 0) {
2299 if (coverStart.to.line - from.line < CHUNK_SIZE) {
2300 array.shift().clear();
2301 coverRange(cm, from, coverStart.to, 0);
2303 coverRange(cm, from, coverStart.from, 0);
2307 while (cmp(to, coverEnd.to) < 0) {
2308 array.pop().clear();
2309 coverEnd = array[array.length - 1].find();
2311 if (cmp(to, coverEnd.to) > 0) {
2312 if (to.line - coverEnd.from.line < CHUNK_SIZE) {
2313 array.pop().clear();
2314 coverRange(cm, coverEnd.from, to);
2316 coverRange(cm, coverEnd.to, to);
2322 },{"../../lib/codemirror":10}],10:[function(require,module,exports){
2323 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2324 // Distributed under an MIT license: http://codemirror.net/LICENSE
2326 // This is CodeMirror (http://codemirror.net), a code editor
2327 // implemented in JavaScript on top of the browser's DOM.
2329 // You can find some technical background for some of the code below
2330 // at http://marijnhaverbeke.nl/blog/#cm-internals .
2333 if (typeof exports == "object" && typeof module == "object") // CommonJS
2334 module.exports = mod();
2335 else if (typeof define == "function" && define.amd) // AMD
2336 return define([], mod);
2337 else // Plain browser env
2338 (this || window).CodeMirror = mod();
2344 // Kludges for bugs and behavior differences that can't be feature
2345 // detected are enabled based on userAgent etc sniffing.
2346 var userAgent = navigator.userAgent;
2347 var platform = navigator.platform;
2349 var gecko = /gecko\/\d/i.test(userAgent);
2350 var ie_upto10 = /MSIE \d/.test(userAgent);
2351 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
2352 var ie = ie_upto10 || ie_11up;
2353 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
2354 var webkit = /WebKit\//.test(userAgent);
2355 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
2356 var chrome = /Chrome\//.test(userAgent);
2357 var presto = /Opera\//.test(userAgent);
2358 var safari = /Apple Computer/.test(navigator.vendor);
2359 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
2360 var phantom = /PhantomJS/.test(userAgent);
2362 var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
2363 // This is woefully incomplete. Suggestions for alternative methods welcome.
2364 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
2365 var mac = ios || /Mac/.test(platform);
2366 var chromeOS = /\bCrOS\b/.test(userAgent);
2367 var windows = /win/i.test(platform);
2369 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
2370 if (presto_version) presto_version = Number(presto_version[1]);
2371 if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
2372 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
2373 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
2374 var captureRightClick = gecko || (ie && ie_version >= 9);
2376 // Optimize some code when these features are not used.
2377 var sawReadOnlySpans = false, sawCollapsedSpans = false;
2379 // EDITOR CONSTRUCTOR
2381 // A CodeMirror instance represents an editor. This is the object
2382 // that user code is usually dealing with.
2384 function CodeMirror(place, options) {
2385 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
2387 this.options = options = options ? copyObj(options) : {};
2388 // Determine effective options based on given values and defaults.
2389 copyObj(defaults, options, false);
2390 setGuttersForLineNumbers(options);
2392 var doc = options.value;
2393 if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator);
2396 var input = new CodeMirror.inputStyles[options.inputStyle](this);
2397 var display = this.display = new Display(place, doc, input);
2398 display.wrapper.CodeMirror = this;
2399 updateGutters(this);
2401 if (options.lineWrapping)
2402 this.display.wrapper.className += " CodeMirror-wrap";
2403 if (options.autofocus && !mobile) display.input.focus();
2404 initScrollbars(this);
2407 keyMaps: [], // stores maps added by addKeyMap
2408 overlays: [], // highlighting overlays, as added by addOverlay
2409 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
2411 delayingBlurEvent: false,
2413 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
2414 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
2415 selectingText: false,
2416 draggingText: false,
2417 highlight: new Delayed(), // stores highlight worker timeout
2418 keySeq: null, // Unfinished key sequence
2424 // Override magic textarea content restore that IE sometimes does
2425 // on our hidden textarea on reload
2426 if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);
2428 registerEventHandlers(this);
2429 ensureGlobalHandlers();
2431 startOperation(this);
2432 this.curOp.forceUpdate = true;
2433 attachDoc(this, doc);
2435 if ((options.autofocus && !mobile) || cm.hasFocus())
2436 setTimeout(bind(onFocus, this), 20);
2440 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
2441 optionHandlers[opt](this, options[opt], Init);
2442 maybeUpdateLineNumberWidth(this);
2443 if (options.finishInit) options.finishInit(this);
2444 for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
2446 // Suppress optimizelegibility in Webkit, since it breaks text
2447 // measuring on line wrapping boundaries.
2448 if (webkit && options.lineWrapping &&
2449 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
2450 display.lineDiv.style.textRendering = "auto";
2453 // DISPLAY CONSTRUCTOR
2455 // The display handles the DOM integration, both for input reading
2456 // and content drawing. It holds references to DOM nodes and
2457 // display-related state.
2459 function Display(place, doc, input) {
2463 // Covers bottom-right square when both scrollbars are present.
2464 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
2465 d.scrollbarFiller.setAttribute("cm-not-content", "true");
2466 // Covers bottom of gutter when coverGutterNextToScrollbar is on
2467 // and h scrollbar is present.
2468 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
2469 d.gutterFiller.setAttribute("cm-not-content", "true");
2470 // Will contain the actual code, positioned to cover the viewport.
2471 d.lineDiv = elt("div", null, "CodeMirror-code");
2472 // Elements are added to these to represent selection and cursors.
2473 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
2474 d.cursorDiv = elt("div", null, "CodeMirror-cursors");
2475 // A visibility: hidden element used to find the size of things.
2476 d.measure = elt("div", null, "CodeMirror-measure");
2477 // When lines outside of the viewport are measured, they are drawn in this.
2478 d.lineMeasure = elt("div", null, "CodeMirror-measure");
2479 // Wraps everything that needs to exist inside the vertically-padded coordinate system
2480 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
2481 null, "position: relative; outline: none");
2482 // Moved around its parent to cover visible view.
2483 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
2484 // Set to the height of the document, allowing scrolling.
2485 d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
2486 d.sizerWidth = null;
2487 // Behavior of elts with overflow: auto and padding is
2488 // inconsistent across browsers. This is used to ensure the
2489 // scrollable area is big enough.
2490 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
2491 // Will contain the gutters, if any.
2492 d.gutters = elt("div", null, "CodeMirror-gutters");
2493 d.lineGutter = null;
2494 // Actual scrollable element.
2495 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
2496 d.scroller.setAttribute("tabIndex", "-1");
2497 // The element in which the editor lives.
2498 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
2500 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
2501 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
2502 if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;
2505 if (place.appendChild) place.appendChild(d.wrapper);
2506 else place(d.wrapper);
2509 // Current rendered range (may be bigger than the view window).
2510 d.viewFrom = d.viewTo = doc.first;
2511 d.reportedViewFrom = d.reportedViewTo = doc.first;
2512 // Information about the rendered lines.
2514 d.renderedView = null;
2515 // Holds info about a single rendered line when it was rendered
2516 // for measurement, while not in view.
2517 d.externalMeasured = null;
2518 // Empty space (in pixels) above the view
2520 d.lastWrapHeight = d.lastWrapWidth = 0;
2521 d.updateLineNumbers = null;
2523 d.nativeBarWidth = d.barHeight = d.barWidth = 0;
2524 d.scrollbarsClipped = false;
2526 // Used to only resize the line number gutter when necessary (when
2527 // the amount of lines crosses a boundary that makes its width change)
2528 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
2529 // Set to true when a non-horizontal-scrolling line widget is
2530 // added. As an optimization, line widget aligning is skipped when
2532 d.alignWidgets = false;
2534 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
2536 // Tracks the maximum line length so that the horizontal scrollbar
2537 // can be kept static when scrolling.
2539 d.maxLineLength = 0;
2540 d.maxLineChanged = false;
2542 // Used for measuring wheel scrolling granularity
2543 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
2545 // True when shift is held down.
2548 // Used to track whether anything happened since the context menu
2550 d.selForContextMenu = null;
2552 d.activeTouch = null;
2559 // Used to get the editor into a consistent state again when options change.
2561 function loadMode(cm) {
2562 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
2566 function resetModeState(cm) {
2567 cm.doc.iter(function(line) {
2568 if (line.stateAfter) line.stateAfter = null;
2569 if (line.styles) line.styles = null;
2571 cm.doc.frontier = cm.doc.first;
2572 startWorker(cm, 100);
2574 if (cm.curOp) regChange(cm);
2577 function wrappingChanged(cm) {
2578 if (cm.options.lineWrapping) {
2579 addClass(cm.display.wrapper, "CodeMirror-wrap");
2580 cm.display.sizer.style.minWidth = "";
2581 cm.display.sizerWidth = null;
2583 rmClass(cm.display.wrapper, "CodeMirror-wrap");
2586 estimateLineHeights(cm);
2589 setTimeout(function(){updateScrollbars(cm);}, 100);
2592 // Returns a function that estimates the height of a line, to use as
2593 // first approximation until the line becomes visible (and is thus
2594 // properly measurable).
2595 function estimateHeight(cm) {
2596 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
2597 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
2598 return function(line) {
2599 if (lineIsHidden(cm.doc, line)) return 0;
2601 var widgetsHeight = 0;
2602 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
2603 if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
2607 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
2609 return widgetsHeight + th;
2613 function estimateLineHeights(cm) {
2614 var doc = cm.doc, est = estimateHeight(cm);
2615 doc.iter(function(line) {
2616 var estHeight = est(line);
2617 if (estHeight != line.height) updateLineHeight(line, estHeight);
2621 function themeChanged(cm) {
2622 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
2623 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
2627 function guttersChanged(cm) {
2630 setTimeout(function(){alignHorizontally(cm);}, 20);
2633 // Rebuild the gutter elements, ensure the margin to the left of the
2634 // code matches their width.
2635 function updateGutters(cm) {
2636 var gutters = cm.display.gutters, specs = cm.options.gutters;
2637 removeChildren(gutters);
2638 for (var i = 0; i < specs.length; ++i) {
2639 var gutterClass = specs[i];
2640 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
2641 if (gutterClass == "CodeMirror-linenumbers") {
2642 cm.display.lineGutter = gElt;
2643 gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
2646 gutters.style.display = i ? "" : "none";
2647 updateGutterSpace(cm);
2650 function updateGutterSpace(cm) {
2651 var width = cm.display.gutters.offsetWidth;
2652 cm.display.sizer.style.marginLeft = width + "px";
2655 // Compute the character length of a line, taking into account
2656 // collapsed ranges (see markText) that might hide parts, and join
2657 // other lines onto it.
2658 function lineLength(line) {
2659 if (line.height == 0) return 0;
2660 var len = line.text.length, merged, cur = line;
2661 while (merged = collapsedSpanAtStart(cur)) {
2662 var found = merged.find(0, true);
2663 cur = found.from.line;
2664 len += found.from.ch - found.to.ch;
2667 while (merged = collapsedSpanAtEnd(cur)) {
2668 var found = merged.find(0, true);
2669 len -= cur.text.length - found.from.ch;
2670 cur = found.to.line;
2671 len += cur.text.length - found.to.ch;
2676 // Find the longest line in the document.
2677 function findMaxLine(cm) {
2678 var d = cm.display, doc = cm.doc;
2679 d.maxLine = getLine(doc, doc.first);
2680 d.maxLineLength = lineLength(d.maxLine);
2681 d.maxLineChanged = true;
2682 doc.iter(function(line) {
2683 var len = lineLength(line);
2684 if (len > d.maxLineLength) {
2685 d.maxLineLength = len;
2691 // Make sure the gutters options contains the element
2692 // "CodeMirror-linenumbers" when the lineNumbers option is true.
2693 function setGuttersForLineNumbers(options) {
2694 var found = indexOf(options.gutters, "CodeMirror-linenumbers");
2695 if (found == -1 && options.lineNumbers) {
2696 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
2697 } else if (found > -1 && !options.lineNumbers) {
2698 options.gutters = options.gutters.slice(0);
2699 options.gutters.splice(found, 1);
2705 // Prepare DOM reads needed to update the scrollbars. Done in one
2706 // shot to minimize update/measure roundtrips.
2707 function measureForScrollbars(cm) {
2708 var d = cm.display, gutterW = d.gutters.offsetWidth;
2709 var docH = Math.round(cm.doc.height + paddingVert(cm.display));
2711 clientHeight: d.scroller.clientHeight,
2712 viewHeight: d.wrapper.clientHeight,
2713 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
2714 viewWidth: d.wrapper.clientWidth,
2715 barLeft: cm.options.fixedGutter ? gutterW : 0,
2717 scrollHeight: docH + scrollGap(cm) + d.barHeight,
2718 nativeBarWidth: d.nativeBarWidth,
2719 gutterWidth: gutterW
2723 function NativeScrollbars(place, scroll, cm) {
2725 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
2726 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
2727 place(vert); place(horiz);
2729 on(vert, "scroll", function() {
2730 if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
2732 on(horiz, "scroll", function() {
2733 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
2736 this.checkedZeroWidth = false;
2737 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
2738 if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
2741 NativeScrollbars.prototype = copyObj({
2742 update: function(measure) {
2743 var needsH = measure.scrollWidth > measure.clientWidth + 1;
2744 var needsV = measure.scrollHeight > measure.clientHeight + 1;
2745 var sWidth = measure.nativeBarWidth;
2748 this.vert.style.display = "block";
2749 this.vert.style.bottom = needsH ? sWidth + "px" : "0";
2750 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
2751 // A bug in IE8 can cause this value to be negative, so guard it.
2752 this.vert.firstChild.style.height =
2753 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
2755 this.vert.style.display = "";
2756 this.vert.firstChild.style.height = "0";
2760 this.horiz.style.display = "block";
2761 this.horiz.style.right = needsV ? sWidth + "px" : "0";
2762 this.horiz.style.left = measure.barLeft + "px";
2763 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
2764 this.horiz.firstChild.style.width =
2765 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
2767 this.horiz.style.display = "";
2768 this.horiz.firstChild.style.width = "0";
2771 if (!this.checkedZeroWidth && measure.clientHeight > 0) {
2772 if (sWidth == 0) this.zeroWidthHack();
2773 this.checkedZeroWidth = true;
2776 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
2778 setScrollLeft: function(pos) {
2779 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
2780 if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz);
2782 setScrollTop: function(pos) {
2783 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
2784 if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert);
2786 zeroWidthHack: function() {
2787 var w = mac && !mac_geMountainLion ? "12px" : "18px";
2788 this.horiz.style.height = this.vert.style.width = w;
2789 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
2790 this.disableHoriz = new Delayed;
2791 this.disableVert = new Delayed;
2793 enableZeroWidthBar: function(bar, delay) {
2794 bar.style.pointerEvents = "auto";
2795 function maybeDisable() {
2796 // To find out whether the scrollbar is still visible, we
2797 // check whether the element under the pixel in the bottom
2798 // left corner of the scrollbar box is the scrollbar box
2799 // itself (when the bar is still visible) or its filler child
2800 // (when the bar is hidden). If it is still visible, we keep
2801 // it enabled, if it's hidden, we disable pointer events.
2802 var box = bar.getBoundingClientRect();
2803 var elt = document.elementFromPoint(box.left + 1, box.bottom - 1);
2804 if (elt != bar) bar.style.pointerEvents = "none";
2805 else delay.set(1000, maybeDisable);
2807 delay.set(1000, maybeDisable);
2810 var parent = this.horiz.parentNode;
2811 parent.removeChild(this.horiz);
2812 parent.removeChild(this.vert);
2814 }, NativeScrollbars.prototype);
2816 function NullScrollbars() {}
2818 NullScrollbars.prototype = copyObj({
2819 update: function() { return {bottom: 0, right: 0}; },
2820 setScrollLeft: function() {},
2821 setScrollTop: function() {},
2822 clear: function() {}
2823 }, NullScrollbars.prototype);
2825 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
2827 function initScrollbars(cm) {
2828 if (cm.display.scrollbars) {
2829 cm.display.scrollbars.clear();
2830 if (cm.display.scrollbars.addClass)
2831 rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
2834 cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
2835 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
2836 // Prevent clicks in the scrollbars from killing focus
2837 on(node, "mousedown", function() {
2838 if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
2840 node.setAttribute("cm-not-content", "true");
2841 }, function(pos, axis) {
2842 if (axis == "horizontal") setScrollLeft(cm, pos);
2843 else setScrollTop(cm, pos);
2845 if (cm.display.scrollbars.addClass)
2846 addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
2849 function updateScrollbars(cm, measure) {
2850 if (!measure) measure = measureForScrollbars(cm);
2851 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
2852 updateScrollbarsInner(cm, measure);
2853 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
2854 if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
2855 updateHeightsInViewport(cm);
2856 updateScrollbarsInner(cm, measureForScrollbars(cm));
2857 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
2861 // Re-synchronize the fake scrollbars with the actual size of the
2863 function updateScrollbarsInner(cm, measure) {
2865 var sizes = d.scrollbars.update(measure);
2867 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
2868 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
2869 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"
2871 if (sizes.right && sizes.bottom) {
2872 d.scrollbarFiller.style.display = "block";
2873 d.scrollbarFiller.style.height = sizes.bottom + "px";
2874 d.scrollbarFiller.style.width = sizes.right + "px";
2875 } else d.scrollbarFiller.style.display = "";
2876 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
2877 d.gutterFiller.style.display = "block";
2878 d.gutterFiller.style.height = sizes.bottom + "px";
2879 d.gutterFiller.style.width = measure.gutterWidth + "px";
2880 } else d.gutterFiller.style.display = "";
2883 // Compute the lines that are visible in a given viewport (defaults
2884 // the the current scroll position). viewport may contain top,
2885 // height, and ensure (see op.scrollToPos) properties.
2886 function visibleLines(display, doc, viewport) {
2887 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
2888 top = Math.floor(top - paddingTop(display));
2889 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
2891 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
2892 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
2893 // forces those lines into the viewport (if possible).
2894 if (viewport && viewport.ensure) {
2895 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
2896 if (ensureFrom < from) {
2898 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
2899 } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
2900 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
2904 return {from: from, to: Math.max(to, from + 1)};
2909 // Re-align line numbers and gutter marks to compensate for
2910 // horizontal scrolling.
2911 function alignHorizontally(cm) {
2912 var display = cm.display, view = display.view;
2913 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
2914 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
2915 var gutterW = display.gutters.offsetWidth, left = comp + "px";
2916 for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
2917 if (cm.options.fixedGutter && view[i].gutter)
2918 view[i].gutter.style.left = left;
2919 var align = view[i].alignable;
2920 if (align) for (var j = 0; j < align.length; j++)
2921 align[j].style.left = left;
2923 if (cm.options.fixedGutter)
2924 display.gutters.style.left = (comp + gutterW) + "px";
2927 // Used to ensure that the line number gutter is still the right
2928 // size for the current document size. Returns true when an update
2930 function maybeUpdateLineNumberWidth(cm) {
2931 if (!cm.options.lineNumbers) return false;
2932 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
2933 if (last.length != display.lineNumChars) {
2934 var test = display.measure.appendChild(elt("div", [elt("div", last)],
2935 "CodeMirror-linenumber CodeMirror-gutter-elt"));
2936 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
2937 display.lineGutter.style.width = "";
2938 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
2939 display.lineNumWidth = display.lineNumInnerWidth + padding;
2940 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
2941 display.lineGutter.style.width = display.lineNumWidth + "px";
2942 updateGutterSpace(cm);
2948 function lineNumberFor(options, i) {
2949 return String(options.lineNumberFormatter(i + options.firstLineNumber));
2952 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
2953 // but using getBoundingClientRect to get a sub-pixel-accurate
2955 function compensateForHScroll(display) {
2956 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
2961 function DisplayUpdate(cm, viewport, force) {
2962 var display = cm.display;
2964 this.viewport = viewport;
2965 // Store some values that we'll need later (but don't want to force a relayout for)
2966 this.visible = visibleLines(display, cm.doc, viewport);
2967 this.editorIsHidden = !display.wrapper.offsetWidth;
2968 this.wrapperHeight = display.wrapper.clientHeight;
2969 this.wrapperWidth = display.wrapper.clientWidth;
2970 this.oldDisplayWidth = displayWidth(cm);
2972 this.dims = getDimensions(cm);
2976 DisplayUpdate.prototype.signal = function(emitter, type) {
2977 if (hasHandler(emitter, type))
2978 this.events.push(arguments);
2980 DisplayUpdate.prototype.finish = function() {
2981 for (var i = 0; i < this.events.length; i++)
2982 signal.apply(null, this.events[i]);
2985 function maybeClipScrollbars(cm) {
2986 var display = cm.display;
2987 if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
2988 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
2989 display.heightForcer.style.height = scrollGap(cm) + "px";
2990 display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
2991 display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
2992 display.scrollbarsClipped = true;
2996 // Does the actual updating of the line display. Bails out
2997 // (returning false) when there is nothing to be done and forced is
2999 function updateDisplayIfNeeded(cm, update) {
3000 var display = cm.display, doc = cm.doc;
3002 if (update.editorIsHidden) {
3007 // Bail out if the visible area is already rendered and nothing changed.
3008 if (!update.force &&
3009 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
3010 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
3011 display.renderedView == display.view && countDirtyView(cm) == 0)
3014 if (maybeUpdateLineNumberWidth(cm)) {
3016 update.dims = getDimensions(cm);
3019 // Compute a suitable new viewport (from & to)
3020 var end = doc.first + doc.size;
3021 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
3022 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
3023 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
3024 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
3025 if (sawCollapsedSpans) {
3026 from = visualLineNo(cm.doc, from);
3027 to = visualLineEndNo(cm.doc, to);
3030 var different = from != display.viewFrom || to != display.viewTo ||
3031 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
3032 adjustView(cm, from, to);
3034 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
3035 // Position the mover div to align with the current scroll position
3036 cm.display.mover.style.top = display.viewOffset + "px";
3038 var toUpdate = countDirtyView(cm);
3039 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
3040 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
3043 // For big changes, we hide the enclosing element during the
3044 // update, since that speeds up the operations on most browsers.
3045 var focused = activeElt();
3046 if (toUpdate > 4) display.lineDiv.style.display = "none";
3047 patchDisplay(cm, display.updateLineNumbers, update.dims);
3048 if (toUpdate > 4) display.lineDiv.style.display = "";
3049 display.renderedView = display.view;
3050 // There might have been a widget with a focused element that got
3051 // hidden or updated, if so re-focus it.
3052 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();
3054 // Prevent selection and cursors from interfering with the scroll
3055 // width and height.
3056 removeChildren(display.cursorDiv);
3057 removeChildren(display.selectionDiv);
3058 display.gutters.style.height = display.sizer.style.minHeight = 0;
3061 display.lastWrapHeight = update.wrapperHeight;
3062 display.lastWrapWidth = update.wrapperWidth;
3063 startWorker(cm, 400);
3066 display.updateLineNumbers = null;
3071 function postUpdateDisplay(cm, update) {
3072 var viewport = update.viewport;
3074 for (var first = true;; first = false) {
3075 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
3076 // Clip forced viewport to actual scrollable area.
3077 if (viewport && viewport.top != null)
3078 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
3079 // Updated line heights might result in the drawn area not
3080 // actually covering the viewport. Keep looping until it does.
3081 update.visible = visibleLines(cm.display, cm.doc, viewport);
3082 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
3085 if (!updateDisplayIfNeeded(cm, update)) break;
3086 updateHeightsInViewport(cm);
3087 var barMeasure = measureForScrollbars(cm);
3088 updateSelection(cm);
3089 updateScrollbars(cm, barMeasure);
3090 setDocumentHeight(cm, barMeasure);
3093 update.signal(cm, "update", cm);
3094 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
3095 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
3096 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
3100 function updateDisplaySimple(cm, viewport) {
3101 var update = new DisplayUpdate(cm, viewport);
3102 if (updateDisplayIfNeeded(cm, update)) {
3103 updateHeightsInViewport(cm);
3104 postUpdateDisplay(cm, update);
3105 var barMeasure = measureForScrollbars(cm);
3106 updateSelection(cm);
3107 updateScrollbars(cm, barMeasure);
3108 setDocumentHeight(cm, barMeasure);
3113 function setDocumentHeight(cm, measure) {
3114 cm.display.sizer.style.minHeight = measure.docHeight + "px";
3115 cm.display.heightForcer.style.top = measure.docHeight + "px";
3116 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
3119 // Read the actual heights of the rendered lines, and update their
3120 // stored heights to match.
3121 function updateHeightsInViewport(cm) {
3122 var display = cm.display;
3123 var prevBottom = display.lineDiv.offsetTop;
3124 for (var i = 0; i < display.view.length; i++) {
3125 var cur = display.view[i], height;
3126 if (cur.hidden) continue;
3127 if (ie && ie_version < 8) {
3128 var bot = cur.node.offsetTop + cur.node.offsetHeight;
3129 height = bot - prevBottom;
3132 var box = cur.node.getBoundingClientRect();
3133 height = box.bottom - box.top;
3135 var diff = cur.line.height - height;
3136 if (height < 2) height = textHeight(display);
3137 if (diff > .001 || diff < -.001) {
3138 updateLineHeight(cur.line, height);
3139 updateWidgetHeight(cur.line);
3140 if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
3141 updateWidgetHeight(cur.rest[j]);
3146 // Read and store the height of line widgets associated with the
3148 function updateWidgetHeight(line) {
3149 if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
3150 line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight;
3153 // Do a bulk-read of the DOM positions and sizes needed to draw the
3154 // view, so that we don't interleave reading and writing to the DOM.
3155 function getDimensions(cm) {
3156 var d = cm.display, left = {}, width = {};
3157 var gutterLeft = d.gutters.clientLeft;
3158 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
3159 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
3160 width[cm.options.gutters[i]] = n.clientWidth;
3162 return {fixedPos: compensateForHScroll(d),
3163 gutterTotalWidth: d.gutters.offsetWidth,
3166 wrapperWidth: d.wrapper.clientWidth};
3169 // Sync the actual display DOM structure with display.view, removing
3170 // nodes for lines that are no longer in view, and creating the ones
3171 // that are not there yet, and updating the ones that are out of
3173 function patchDisplay(cm, updateNumbersFrom, dims) {
3174 var display = cm.display, lineNumbers = cm.options.lineNumbers;
3175 var container = display.lineDiv, cur = container.firstChild;
3178 var next = node.nextSibling;
3179 // Works around a throw-scroll bug in OS X Webkit
3180 if (webkit && mac && cm.display.currentWheelTarget == node)
3181 node.style.display = "none";
3183 node.parentNode.removeChild(node);
3187 var view = display.view, lineN = display.viewFrom;
3188 // Loop over the elements in the view, syncing cur (the DOM nodes
3189 // in display.lineDiv) with the view as we go.
3190 for (var i = 0; i < view.length; i++) {
3191 var lineView = view[i];
3192 if (lineView.hidden) {
3193 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
3194 var node = buildLineElement(cm, lineView, lineN, dims);
3195 container.insertBefore(node, cur);
3196 } else { // Already drawn
3197 while (cur != lineView.node) cur = rm(cur);
3198 var updateNumber = lineNumbers && updateNumbersFrom != null &&
3199 updateNumbersFrom <= lineN && lineView.lineNumber;
3200 if (lineView.changes) {
3201 if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
3202 updateLineForChanges(cm, lineView, lineN, dims);
3205 removeChildren(lineView.lineNumber);
3206 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
3208 cur = lineView.node.nextSibling;
3210 lineN += lineView.size;
3212 while (cur) cur = rm(cur);
3215 // When an aspect of a line changes, a string is added to
3216 // lineView.changes. This updates the relevant part of the line's
3218 function updateLineForChanges(cm, lineView, lineN, dims) {
3219 for (var j = 0; j < lineView.changes.length; j++) {
3220 var type = lineView.changes[j];
3221 if (type == "text") updateLineText(cm, lineView);
3222 else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
3223 else if (type == "class") updateLineClasses(lineView);
3224 else if (type == "widget") updateLineWidgets(cm, lineView, dims);
3226 lineView.changes = null;
3229 // Lines with gutter elements, widgets or a background class need to
3230 // be wrapped, and have the extra elements added to the wrapper div
3231 function ensureLineWrapped(lineView) {
3232 if (lineView.node == lineView.text) {
3233 lineView.node = elt("div", null, null, "position: relative");
3234 if (lineView.text.parentNode)
3235 lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
3236 lineView.node.appendChild(lineView.text);
3237 if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
3239 return lineView.node;
3242 function updateLineBackground(lineView) {
3243 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
3244 if (cls) cls += " CodeMirror-linebackground";
3245 if (lineView.background) {
3246 if (cls) lineView.background.className = cls;
3247 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
3249 var wrap = ensureLineWrapped(lineView);
3250 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
3254 // Wrapper around buildLineContent which will reuse the structure
3255 // in display.externalMeasured when possible.
3256 function getLineContent(cm, lineView) {
3257 var ext = cm.display.externalMeasured;
3258 if (ext && ext.line == lineView.line) {
3259 cm.display.externalMeasured = null;
3260 lineView.measure = ext.measure;
3263 return buildLineContent(cm, lineView);
3266 // Redraw the line's text. Interacts with the background and text
3267 // classes because the mode may output tokens that influence these
3269 function updateLineText(cm, lineView) {
3270 var cls = lineView.text.className;
3271 var built = getLineContent(cm, lineView);
3272 if (lineView.text == lineView.node) lineView.node = built.pre;
3273 lineView.text.parentNode.replaceChild(built.pre, lineView.text);
3274 lineView.text = built.pre;
3275 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
3276 lineView.bgClass = built.bgClass;
3277 lineView.textClass = built.textClass;
3278 updateLineClasses(lineView);
3280 lineView.text.className = cls;
3284 function updateLineClasses(lineView) {
3285 updateLineBackground(lineView);
3286 if (lineView.line.wrapClass)
3287 ensureLineWrapped(lineView).className = lineView.line.wrapClass;
3288 else if (lineView.node != lineView.text)
3289 lineView.node.className = "";
3290 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
3291 lineView.text.className = textClass || "";
3294 function updateLineGutter(cm, lineView, lineN, dims) {
3295 if (lineView.gutter) {
3296 lineView.node.removeChild(lineView.gutter);
3297 lineView.gutter = null;
3299 if (lineView.gutterBackground) {
3300 lineView.node.removeChild(lineView.gutterBackground);
3301 lineView.gutterBackground = null;
3303 if (lineView.line.gutterClass) {
3304 var wrap = ensureLineWrapped(lineView);
3305 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
3306 "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
3307 "px; width: " + dims.gutterTotalWidth + "px");
3308 wrap.insertBefore(lineView.gutterBackground, lineView.text);
3310 var markers = lineView.line.gutterMarkers;
3311 if (cm.options.lineNumbers || markers) {
3312 var wrap = ensureLineWrapped(lineView);
3313 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
3314 (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px");
3315 cm.display.input.setUneditable(gutterWrap);
3316 wrap.insertBefore(gutterWrap, lineView.text);
3317 if (lineView.line.gutterClass)
3318 gutterWrap.className += " " + lineView.line.gutterClass;
3319 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
3320 lineView.lineNumber = gutterWrap.appendChild(
3321 elt("div", lineNumberFor(cm.options, lineN),
3322 "CodeMirror-linenumber CodeMirror-gutter-elt",
3323 "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
3324 + cm.display.lineNumInnerWidth + "px"));
3325 if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
3326 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
3328 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
3329 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
3334 function updateLineWidgets(cm, lineView, dims) {
3335 if (lineView.alignable) lineView.alignable = null;
3336 for (var node = lineView.node.firstChild, next; node; node = next) {
3337 var next = node.nextSibling;
3338 if (node.className == "CodeMirror-linewidget")
3339 lineView.node.removeChild(node);
3341 insertLineWidgets(cm, lineView, dims);
3344 // Build a line's DOM representation from scratch
3345 function buildLineElement(cm, lineView, lineN, dims) {
3346 var built = getLineContent(cm, lineView);
3347 lineView.text = lineView.node = built.pre;
3348 if (built.bgClass) lineView.bgClass = built.bgClass;
3349 if (built.textClass) lineView.textClass = built.textClass;
3351 updateLineClasses(lineView);
3352 updateLineGutter(cm, lineView, lineN, dims);
3353 insertLineWidgets(cm, lineView, dims);
3354 return lineView.node;
3357 // A lineView may contain multiple logical lines (when merged by
3358 // collapsed spans). The widgets for all of them need to be drawn.
3359 function insertLineWidgets(cm, lineView, dims) {
3360 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
3361 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
3362 insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
3365 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
3366 if (!line.widgets) return;
3367 var wrap = ensureLineWrapped(lineView);
3368 for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
3369 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
3370 if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
3371 positionLineWidget(widget, node, lineView, dims);
3372 cm.display.input.setUneditable(node);
3373 if (allowAbove && widget.above)
3374 wrap.insertBefore(node, lineView.gutter || lineView.text);
3376 wrap.appendChild(node);
3377 signalLater(widget, "redraw");
3381 function positionLineWidget(widget, node, lineView, dims) {
3382 if (widget.noHScroll) {
3383 (lineView.alignable || (lineView.alignable = [])).push(node);
3384 var width = dims.wrapperWidth;
3385 node.style.left = dims.fixedPos + "px";
3386 if (!widget.coverGutter) {
3387 width -= dims.gutterTotalWidth;
3388 node.style.paddingLeft = dims.gutterTotalWidth + "px";
3390 node.style.width = width + "px";
3392 if (widget.coverGutter) {
3393 node.style.zIndex = 5;
3394 node.style.position = "relative";
3395 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
3401 // A Pos instance represents a position within the text.
3402 var Pos = CodeMirror.Pos = function(line, ch) {
3403 if (!(this instanceof Pos)) return new Pos(line, ch);
3404 this.line = line; this.ch = ch;
3407 // Compare two positions, return 0 if they are the same, a negative
3408 // number when a is less, and a positive number otherwise.
3409 var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
3411 function copyPos(x) {return Pos(x.line, x.ch);}
3412 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
3413 function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
3417 function ensureFocus(cm) {
3418 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
3421 // This will be set to a {lineWise: bool, text: [string]} object, so
3422 // that, when pasting, we know what kind of selections the copied
3423 // text was made out of.
3424 var lastCopied = null;
3426 function applyTextInput(cm, inserted, deleted, sel, origin) {
3428 cm.display.shift = false;
3429 if (!sel) sel = doc.sel;
3431 var paste = cm.state.pasteIncoming || origin == "paste";
3432 var textLines = doc.splitLines(inserted), multiPaste = null
3433 // When pasing N lines into N selections, insert one line per selection
3434 if (paste && sel.ranges.length > 1) {
3435 if (lastCopied && lastCopied.text.join("\n") == inserted) {
3436 if (sel.ranges.length % lastCopied.text.length == 0) {
3438 for (var i = 0; i < lastCopied.text.length; i++)
3439 multiPaste.push(doc.splitLines(lastCopied.text[i]));
3441 } else if (textLines.length == sel.ranges.length) {
3442 multiPaste = map(textLines, function(l) { return [l]; });
3446 // Normal behavior is to insert the new text into every selection
3447 for (var i = sel.ranges.length - 1; i >= 0; i--) {
3448 var range = sel.ranges[i];
3449 var from = range.from(), to = range.to();
3450 if (range.empty()) {
3451 if (deleted && deleted > 0) // Handle deletion
3452 from = Pos(from.line, from.ch - deleted);
3453 else if (cm.state.overwrite && !paste) // Handle overwrite
3454 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
3455 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted)
3456 from = to = Pos(from.line, 0)
3458 var updateInput = cm.curOp.updateInput;
3459 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
3460 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
3461 makeChange(cm.doc, changeEvent);
3462 signalLater(cm, "inputRead", cm, changeEvent);
3464 if (inserted && !paste)
3465 triggerElectric(cm, inserted);
3467 ensureCursorVisible(cm);
3468 cm.curOp.updateInput = updateInput;
3469 cm.curOp.typing = true;
3470 cm.state.pasteIncoming = cm.state.cutIncoming = false;
3473 function handlePaste(e, cm) {
3474 var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
3477 if (!cm.isReadOnly() && !cm.options.disableInput)
3478 runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
3483 function triggerElectric(cm, inserted) {
3484 // When an 'electric' character is inserted, immediately trigger a reindent
3485 if (!cm.options.electricChars || !cm.options.smartIndent) return;
3486 var sel = cm.doc.sel;
3488 for (var i = sel.ranges.length - 1; i >= 0; i--) {
3489 var range = sel.ranges[i];
3490 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;
3491 var mode = cm.getModeAt(range.head);
3492 var indented = false;
3493 if (mode.electricChars) {
3494 for (var j = 0; j < mode.electricChars.length; j++)
3495 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
3496 indented = indentLine(cm, range.head.line, "smart");
3499 } else if (mode.electricInput) {
3500 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
3501 indented = indentLine(cm, range.head.line, "smart");
3503 if (indented) signalLater(cm, "electricInput", cm, range.head.line);
3507 function copyableRanges(cm) {
3508 var text = [], ranges = [];
3509 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
3510 var line = cm.doc.sel.ranges[i].head.line;
3511 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
3512 ranges.push(lineRange);
3513 text.push(cm.getRange(lineRange.anchor, lineRange.head));
3515 return {text: text, ranges: ranges};
3518 function disableBrowserMagic(field) {
3519 field.setAttribute("autocorrect", "off");
3520 field.setAttribute("autocapitalize", "off");
3521 field.setAttribute("spellcheck", "false");
3524 // TEXTAREA INPUT STYLE
3526 function TextareaInput(cm) {
3528 // See input.poll and input.reset
3529 this.prevInput = "";
3531 // Flag that indicates whether we expect input to appear real soon
3532 // now (after some event like 'keypress' or 'input') and are
3533 // polling intensively.
3534 this.pollingFast = false;
3535 // Self-resetting timeout for the poller
3536 this.polling = new Delayed();
3537 // Tracks when input.reset has punted to just putting a short
3538 // string into the textarea instead of the full selection.
3539 this.inaccurateSelection = false;
3540 // Used to work around IE issue with selection being forgotten when focus moves away from textarea
3541 this.hasSelection = false;
3542 this.composing = null;
3545 function hiddenTextarea() {
3546 var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
3547 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
3548 // The textarea is kept positioned near the cursor to prevent the
3549 // fact that it'll be scrolled into view on input from scrolling
3550 // our fake cursor out of view. On webkit, when wrap=off, paste is
3551 // very slow. So make the area wide instead.
3552 if (webkit) te.style.width = "1000px";
3553 else te.setAttribute("wrap", "off");
3554 // If border: 0; -- iOS fails to open keyboard (issue #1287)
3555 if (ios) te.style.border = "1px solid black";
3556 disableBrowserMagic(te);
3560 TextareaInput.prototype = copyObj({
3561 init: function(display) {
3562 var input = this, cm = this.cm;
3564 // Wraps and hides input textarea
3565 var div = this.wrapper = hiddenTextarea();
3566 // The semihidden textarea that is focused when the editor is
3567 // focused, and receives input.
3568 var te = this.textarea = div.firstChild;
3569 display.wrapper.insertBefore(div, display.wrapper.firstChild);
3571 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
3572 if (ios) te.style.width = "0px";
3574 on(te, "input", function() {
3575 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
3579 on(te, "paste", function(e) {
3580 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return
3582 cm.state.pasteIncoming = true;
3586 function prepareCopyCut(e) {
3587 if (signalDOMEvent(cm, e)) return
3588 if (cm.somethingSelected()) {
3589 lastCopied = {lineWise: false, text: cm.getSelections()};
3590 if (input.inaccurateSelection) {
3591 input.prevInput = "";
3592 input.inaccurateSelection = false;
3593 te.value = lastCopied.text.join("\n");
3596 } else if (!cm.options.lineWiseCopyCut) {
3599 var ranges = copyableRanges(cm);
3600 lastCopied = {lineWise: true, text: ranges.text};
3601 if (e.type == "cut") {
3602 cm.setSelections(ranges.ranges, null, sel_dontScroll);
3604 input.prevInput = "";
3605 te.value = ranges.text.join("\n");
3609 if (e.type == "cut") cm.state.cutIncoming = true;
3611 on(te, "cut", prepareCopyCut);
3612 on(te, "copy", prepareCopyCut);
3614 on(display.scroller, "paste", function(e) {
3615 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return;
3616 cm.state.pasteIncoming = true;
3620 // Prevent normal selection in the editor (we handle our own)
3621 on(display.lineSpace, "selectstart", function(e) {
3622 if (!eventInWidget(display, e)) e_preventDefault(e);
3625 on(te, "compositionstart", function() {
3626 var start = cm.getCursor("from");
3627 if (input.composing) input.composing.range.clear()
3630 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
3633 on(te, "compositionend", function() {
3634 if (input.composing) {
3636 input.composing.range.clear();
3637 input.composing = null;
3642 prepareSelection: function() {
3643 // Redraw the selection and/or cursor
3644 var cm = this.cm, display = cm.display, doc = cm.doc;
3645 var result = prepareSelection(cm);
3647 // Move the hidden textarea near the cursor to prevent scrolling artifacts
3648 if (cm.options.moveInputWithCursor) {
3649 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
3650 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
3651 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
3652 headPos.top + lineOff.top - wrapOff.top));
3653 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
3654 headPos.left + lineOff.left - wrapOff.left));
3660 showSelection: function(drawn) {
3661 var cm = this.cm, display = cm.display;
3662 removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
3663 removeChildrenAndAdd(display.selectionDiv, drawn.selection);
3664 if (drawn.teTop != null) {
3665 this.wrapper.style.top = drawn.teTop + "px";
3666 this.wrapper.style.left = drawn.teLeft + "px";
3670 // Reset the input to correspond to the selection (or to be empty,
3671 // when not typing and nothing is selected)
3672 reset: function(typing) {
3673 if (this.contextMenuPending) return;
3674 var minimal, selected, cm = this.cm, doc = cm.doc;
3675 if (cm.somethingSelected()) {
3676 this.prevInput = "";
3677 var range = doc.sel.primary();
3678 minimal = hasCopyEvent &&
3679 (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
3680 var content = minimal ? "-" : selected || cm.getSelection();
3681 this.textarea.value = content;
3682 if (cm.state.focused) selectInput(this.textarea);
3683 if (ie && ie_version >= 9) this.hasSelection = content;
3684 } else if (!typing) {
3685 this.prevInput = this.textarea.value = "";
3686 if (ie && ie_version >= 9) this.hasSelection = null;
3688 this.inaccurateSelection = minimal;
3691 getField: function() { return this.textarea; },
3693 supportsTouch: function() { return false; },
3696 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
3697 try { this.textarea.focus(); }
3698 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
3702 blur: function() { this.textarea.blur(); },
3704 resetPosition: function() {
3705 this.wrapper.style.top = this.wrapper.style.left = 0;
3708 receivedFocus: function() { this.slowPoll(); },
3710 // Poll for input changes, using the normal rate of polling. This
3711 // runs as long as the editor is focused.
3712 slowPoll: function() {
3714 if (input.pollingFast) return;
3715 input.polling.set(this.cm.options.pollInterval, function() {
3717 if (input.cm.state.focused) input.slowPoll();
3721 // When an event has just come in that is likely to add or change
3722 // something in the input textarea, we poll faster, to ensure that
3723 // the change appears on the screen quickly.
3724 fastPoll: function() {
3725 var missed = false, input = this;
3726 input.pollingFast = true;
3728 var changed = input.poll();
3729 if (!changed && !missed) {missed = true; input.polling.set(60, p);}
3730 else {input.pollingFast = false; input.slowPoll();}
3732 input.polling.set(20, p);
3735 // Read input from the textarea, and update the document to match.
3736 // When something is selected, it is present in the textarea, and
3737 // selected (unless it is huge, in which case a placeholder is
3738 // used). When nothing is selected, the cursor sits after previously
3739 // seen text (can be empty), which is stored in prevInput (we must
3740 // not reset the textarea when typing, because that breaks IME).
3742 var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
3743 // Since this is called a *lot*, try to bail out as cheaply as
3744 // possible when it is clear that nothing happened. hasSelection
3745 // will be the case when there is a lot of text in the textarea,
3746 // in which case reading its value would be expensive.
3747 if (this.contextMenuPending || !cm.state.focused ||
3748 (hasSelection(input) && !prevInput && !this.composing) ||
3749 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
3752 var text = input.value;
3753 // If nothing changed, bail.
3754 if (text == prevInput && !cm.somethingSelected()) return false;
3755 // Work around nonsensical selection resetting in IE9/10, and
3756 // inexplicable appearance of private area unicode characters on
3757 // some key combos in Mac (#2689).
3758 if (ie && ie_version >= 9 && this.hasSelection === text ||
3759 mac && /[\uf700-\uf7ff]/.test(text)) {
3760 cm.display.input.reset();
3764 if (cm.doc.sel == cm.display.selForContextMenu) {
3765 var first = text.charCodeAt(0);
3766 if (first == 0x200b && !prevInput) prevInput = "\u200b";
3767 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
3769 // Find the part of the input that is actually new
3770 var same = 0, l = Math.min(prevInput.length, text.length);
3771 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
3774 runInOp(cm, function() {
3775 applyTextInput(cm, text.slice(same), prevInput.length - same,
3776 null, self.composing ? "*compose" : null);
3778 // Don't leave long text in the textarea, since it makes further polling slow
3779 if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
3780 else self.prevInput = text;
3782 if (self.composing) {
3783 self.composing.range.clear();
3784 self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
3785 {className: "CodeMirror-composing"});
3791 ensurePolled: function() {
3792 if (this.pollingFast && this.poll()) this.pollingFast = false;
3795 onKeyPress: function() {
3796 if (ie && ie_version >= 9) this.hasSelection = null;
3800 onContextMenu: function(e) {
3801 var input = this, cm = input.cm, display = cm.display, te = input.textarea;
3802 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
3803 if (!pos || presto) return; // Opera is difficult.
3805 // Reset the current text selection only if the click is done outside of the selection
3806 // and 'resetSelectionOnContextMenu' option is true.
3807 var reset = cm.options.resetSelectionOnContextMenu;
3808 if (reset && cm.doc.sel.contains(pos) == -1)
3809 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
3811 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
3812 input.wrapper.style.cssText = "position: absolute"
3813 var wrapperBox = input.wrapper.getBoundingClientRect()
3814 te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) +
3815 "px; left: " + (e.clientX - wrapperBox.left - 5) + "px; z-index: 1000; background: " +
3816 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
3817 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
3818 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
3819 display.input.focus();
3820 if (webkit) window.scrollTo(null, oldScrollY);
3821 display.input.reset();
3822 // Adds "Select all" to context menu in FF
3823 if (!cm.somethingSelected()) te.value = input.prevInput = " ";
3824 input.contextMenuPending = true;
3825 display.selForContextMenu = cm.doc.sel;
3826 clearTimeout(display.detectingSelectAll);
3828 // Select-all will be greyed out if there's nothing to select, so
3829 // this adds a zero-width space so that we can later check whether
3831 function prepareSelectAllHack() {
3832 if (te.selectionStart != null) {
3833 var selected = cm.somethingSelected();
3834 var extval = "\u200b" + (selected ? te.value : "");
3835 te.value = "\u21da"; // Used to catch context-menu undo
3837 input.prevInput = selected ? "" : "\u200b";
3838 te.selectionStart = 1; te.selectionEnd = extval.length;
3839 // Re-set this, in case some other handler touched the
3840 // selection in the meantime.
3841 display.selForContextMenu = cm.doc.sel;
3845 input.contextMenuPending = false;
3846 input.wrapper.style.cssText = oldWrapperCSS
3847 te.style.cssText = oldCSS;
3848 if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);
3850 // Try to detect the user choosing select-all
3851 if (te.selectionStart != null) {
3852 if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
3853 var i = 0, poll = function() {
3854 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
3855 te.selectionEnd > 0 && input.prevInput == "\u200b")
3856 operation(cm, commands.selectAll)(cm);
3857 else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
3858 else display.input.reset();
3860 display.detectingSelectAll = setTimeout(poll, 200);
3864 if (ie && ie_version >= 9) prepareSelectAllHack();
3865 if (captureRightClick) {
3867 var mouseup = function() {
3868 off(window, "mouseup", mouseup);
3869 setTimeout(rehide, 20);
3871 on(window, "mouseup", mouseup);
3873 setTimeout(rehide, 50);
3877 readOnlyChanged: function(val) {
3878 if (!val) this.reset();
3881 setUneditable: nothing,
3883 needsContentAttribute: false
3884 }, TextareaInput.prototype);
3886 // CONTENTEDITABLE INPUT STYLE
3888 function ContentEditableInput(cm) {
3890 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
3891 this.polling = new Delayed();
3892 this.gracePeriod = false;
3895 ContentEditableInput.prototype = copyObj({
3896 init: function(display) {
3897 var input = this, cm = input.cm;
3898 var div = input.div = display.lineDiv;
3899 disableBrowserMagic(div);
3901 on(div, "paste", function(e) {
3902 if (!signalDOMEvent(cm, e)) handlePaste(e, cm);
3905 on(div, "compositionstart", function(e) {
3907 input.composing = {sel: cm.doc.sel, data: data, startData: data};
3909 var prim = cm.doc.sel.primary();
3910 var line = cm.getLine(prim.head.line);
3911 var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
3912 if (found > -1 && found <= prim.head.ch)
3913 input.composing.sel = simpleSelection(Pos(prim.head.line, found),
3914 Pos(prim.head.line, found + data.length));
3916 on(div, "compositionupdate", function(e) {
3917 input.composing.data = e.data;
3919 on(div, "compositionend", function(e) {
3920 var ours = input.composing;
3922 if (e.data != ours.startData && !/\u200b/.test(e.data))
3924 // Need a small delay to prevent other code (input event,
3925 // selection polling) from doing damage when fired right after
3927 setTimeout(function() {
3929 input.applyComposition(ours);
3930 if (input.composing == ours)
3931 input.composing = null;
3935 on(div, "touchstart", function() {
3936 input.forceCompositionEnd();
3939 on(div, "input", function() {
3940 if (input.composing) return;
3941 if (cm.isReadOnly() || !input.pollContent())
3942 runInOp(input.cm, function() {regChange(cm);});
3945 function onCopyCut(e) {
3946 if (signalDOMEvent(cm, e)) return
3947 if (cm.somethingSelected()) {
3948 lastCopied = {lineWise: false, text: cm.getSelections()};
3949 if (e.type == "cut") cm.replaceSelection("", null, "cut");
3950 } else if (!cm.options.lineWiseCopyCut) {
3953 var ranges = copyableRanges(cm);
3954 lastCopied = {lineWise: true, text: ranges.text};
3955 if (e.type == "cut") {
3956 cm.operation(function() {
3957 cm.setSelections(ranges.ranges, 0, sel_dontScroll);
3958 cm.replaceSelection("", null, "cut");
3962 // iOS exposes the clipboard API, but seems to discard content inserted into it
3963 if (e.clipboardData && !ios) {
3965 e.clipboardData.clearData();
3966 e.clipboardData.setData("text/plain", lastCopied.text.join("\n"));
3968 // Old-fashioned briefly-focus-a-textarea hack
3969 var kludge = hiddenTextarea(), te = kludge.firstChild;
3970 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
3971 te.value = lastCopied.text.join("\n");
3972 var hadFocus = document.activeElement;
3974 setTimeout(function() {
3975 cm.display.lineSpace.removeChild(kludge);
3980 on(div, "copy", onCopyCut);
3981 on(div, "cut", onCopyCut);
3984 prepareSelection: function() {
3985 var result = prepareSelection(this.cm, false);
3986 result.focus = this.cm.state.focused;
3990 showSelection: function(info, takeFocus) {
3991 if (!info || !this.cm.display.view.length) return;
3992 if (info.focus || takeFocus) this.showPrimarySelection();
3993 this.showMultipleSelections(info);
3996 showPrimarySelection: function() {
3997 var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
3998 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
3999 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
4000 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
4001 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
4002 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
4005 var start = posToDOM(this.cm, prim.from());
4006 var end = posToDOM(this.cm, prim.to());
4007 if (!start && !end) return;
4009 var view = this.cm.display.view;
4010 var old = sel.rangeCount && sel.getRangeAt(0);
4012 start = {node: view[0].measure.map[2], offset: 0};
4013 } else if (!end) { // FIXME dangerously hacky
4014 var measure = view[view.length - 1].measure;
4015 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
4016 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
4019 try { var rng = range(start.node, start.offset, end.offset, end.node); }
4020 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
4022 if (!gecko && this.cm.state.focused) {
4023 sel.collapse(start.node, start.offset);
4024 if (!rng.collapsed) sel.addRange(rng);
4026 sel.removeAllRanges();
4029 if (old && sel.anchorNode == null) sel.addRange(old);
4030 else if (gecko) this.startGracePeriod();
4032 this.rememberSelection();
4035 startGracePeriod: function() {
4037 clearTimeout(this.gracePeriod);
4038 this.gracePeriod = setTimeout(function() {
4039 input.gracePeriod = false;
4040 if (input.selectionChanged())
4041 input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
4045 showMultipleSelections: function(info) {
4046 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
4047 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
4050 rememberSelection: function() {
4051 var sel = window.getSelection();
4052 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
4053 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
4056 selectionInEditor: function() {
4057 var sel = window.getSelection();
4058 if (!sel.rangeCount) return false;
4059 var node = sel.getRangeAt(0).commonAncestorContainer;
4060 return contains(this.div, node);
4064 if (this.cm.options.readOnly != "nocursor") this.div.focus();
4066 blur: function() { this.div.blur(); },
4067 getField: function() { return this.div; },
4069 supportsTouch: function() { return true; },
4071 receivedFocus: function() {
4073 if (this.selectionInEditor())
4074 this.pollSelection();
4076 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });
4079 if (input.cm.state.focused) {
4080 input.pollSelection();
4081 input.polling.set(input.cm.options.pollInterval, poll);
4084 this.polling.set(this.cm.options.pollInterval, poll);
4087 selectionChanged: function() {
4088 var sel = window.getSelection();
4089 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
4090 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
4093 pollSelection: function() {
4094 if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
4095 var sel = window.getSelection(), cm = this.cm;
4096 this.rememberSelection();
4097 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
4098 var head = domToPos(cm, sel.focusNode, sel.focusOffset);
4099 if (anchor && head) runInOp(cm, function() {
4100 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
4101 if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
4106 pollContent: function() {
4107 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
4108 var from = sel.from(), to = sel.to();
4109 if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;
4112 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
4113 var fromLine = lineNo(display.view[0].line);
4114 var fromNode = display.view[0].node;
4116 var fromLine = lineNo(display.view[fromIndex].line);
4117 var fromNode = display.view[fromIndex - 1].node.nextSibling;
4119 var toIndex = findViewIndex(cm, to.line);
4120 if (toIndex == display.view.length - 1) {
4121 var toLine = display.viewTo - 1;
4122 var toNode = display.lineDiv.lastChild;
4124 var toLine = lineNo(display.view[toIndex + 1].line) - 1;
4125 var toNode = display.view[toIndex + 1].node.previousSibling;
4128 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
4129 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
4130 while (newText.length > 1 && oldText.length > 1) {
4131 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
4132 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
4136 var cutFront = 0, cutEnd = 0;
4137 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
4138 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
4140 var newBot = lst(newText), oldBot = lst(oldText);
4141 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
4142 oldBot.length - (oldText.length == 1 ? cutFront : 0));
4143 while (cutEnd < maxCutEnd &&
4144 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
4147 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
4148 newText[0] = newText[0].slice(cutFront);
4150 var chFrom = Pos(fromLine, cutFront);
4151 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
4152 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
4153 replaceRange(cm.doc, newText, chFrom, chTo, "+input");
4158 ensurePolled: function() {
4159 this.forceCompositionEnd();
4162 this.forceCompositionEnd();
4164 forceCompositionEnd: function() {
4165 if (!this.composing || this.composing.handled) return;
4166 this.applyComposition(this.composing);
4167 this.composing.handled = true;
4171 applyComposition: function(composing) {
4172 if (this.cm.isReadOnly())
4173 operation(this.cm, regChange)(this.cm)
4174 else if (composing.data && composing.data != composing.startData)
4175 operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
4178 setUneditable: function(node) {
4179 node.contentEditable = "false"
4182 onKeyPress: function(e) {
4184 if (!this.cm.isReadOnly())
4185 operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
4188 readOnlyChanged: function(val) {
4189 this.div.contentEditable = String(val != "nocursor")
4192 onContextMenu: nothing,
4193 resetPosition: nothing,
4195 needsContentAttribute: true
4196 }, ContentEditableInput.prototype);
4198 function posToDOM(cm, pos) {
4199 var view = findViewForLine(cm, pos.line);
4200 if (!view || view.hidden) return null;
4201 var line = getLine(cm.doc, pos.line);
4202 var info = mapFromLineView(view, line, pos.line);
4204 var order = getOrder(line), side = "left";
4206 var partPos = getBidiPartAt(order, pos.ch);
4207 side = partPos % 2 ? "right" : "left";
4209 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
4210 result.offset = result.collapse == "right" ? result.end : result.start;
4214 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
4216 function domToPos(cm, node, offset) {
4218 if (node == cm.display.lineDiv) {
4219 lineNode = cm.display.lineDiv.childNodes[offset];
4220 if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
4221 node = null; offset = 0;
4223 for (lineNode = node;; lineNode = lineNode.parentNode) {
4224 if (!lineNode || lineNode == cm.display.lineDiv) return null;
4225 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
4228 for (var i = 0; i < cm.display.view.length; i++) {
4229 var lineView = cm.display.view[i];
4230 if (lineView.node == lineNode)
4231 return locateNodeInLineView(lineView, node, offset);
4235 function locateNodeInLineView(lineView, node, offset) {
4236 var wrapper = lineView.text.firstChild, bad = false;
4237 if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
4238 if (node == wrapper) {
4240 node = wrapper.childNodes[offset];
4243 var line = lineView.rest ? lst(lineView.rest) : lineView.line;
4244 return badPos(Pos(lineNo(line), line.text.length), bad);
4248 var textNode = node.nodeType == 3 ? node : null, topNode = node;
4249 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
4250 textNode = node.firstChild;
4251 if (offset) offset = textNode.nodeValue.length;
4253 while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
4254 var measure = lineView.measure, maps = measure.maps;
4256 function find(textNode, topNode, offset) {
4257 for (var i = -1; i < (maps ? maps.length : 0); i++) {
4258 var map = i < 0 ? measure.map : maps[i];
4259 for (var j = 0; j < map.length; j += 3) {
4260 var curNode = map[j + 2];
4261 if (curNode == textNode || curNode == topNode) {
4262 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
4263 var ch = map[j] + offset;
4264 if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
4265 return Pos(line, ch);
4270 var found = find(textNode, topNode, offset);
4271 if (found) return badPos(found, bad);
4273 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
4274 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
4275 found = find(after, after.firstChild, 0);
4277 return badPos(Pos(found.line, found.ch - dist), bad);
4279 dist += after.textContent.length;
4281 for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
4282 found = find(before, before.firstChild, -1);
4284 return badPos(Pos(found.line, found.ch + dist), bad);
4286 dist += after.textContent.length;
4290 function domTextBetween(cm, from, to, fromLine, toLine) {
4291 var text = "", closing = false, lineSep = cm.doc.lineSeparator();
4292 function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
4293 function walk(node) {
4294 if (node.nodeType == 1) {
4295 var cmText = node.getAttribute("cm-text");
4296 if (cmText != null) {
4297 if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
4301 var markerID = node.getAttribute("cm-marker"), range;
4303 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
4304 if (found.length && (range = found[0].find()))
4305 text += getBetween(cm.doc, range.from, range.to).join(lineSep);
4308 if (node.getAttribute("contenteditable") == "false") return;
4309 for (var i = 0; i < node.childNodes.length; i++)
4310 walk(node.childNodes[i]);
4311 if (/^(pre|div|p)$/i.test(node.nodeName))
4313 } else if (node.nodeType == 3) {
4314 var val = node.nodeValue;
4325 if (from == to) break;
4326 from = from.nextSibling;
4331 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
4333 // SELECTION / CURSOR
4335 // Selection objects are immutable. A new one is created every time
4336 // the selection changes. A selection is one or more non-overlapping
4337 // (and non-touching) ranges, sorted, and an integer that indicates
4338 // which one is the primary selection (the one that's scrolled into
4339 // view, that getCursor returns, etc).
4340 function Selection(ranges, primIndex) {
4341 this.ranges = ranges;
4342 this.primIndex = primIndex;
4345 Selection.prototype = {
4346 primary: function() { return this.ranges[this.primIndex]; },
4347 equals: function(other) {
4348 if (other == this) return true;
4349 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
4350 for (var i = 0; i < this.ranges.length; i++) {
4351 var here = this.ranges[i], there = other.ranges[i];
4352 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
4356 deepCopy: function() {
4357 for (var out = [], i = 0; i < this.ranges.length; i++)
4358 out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
4359 return new Selection(out, this.primIndex);
4361 somethingSelected: function() {
4362 for (var i = 0; i < this.ranges.length; i++)
4363 if (!this.ranges[i].empty()) return true;
4366 contains: function(pos, end) {
4367 if (!end) end = pos;
4368 for (var i = 0; i < this.ranges.length; i++) {
4369 var range = this.ranges[i];
4370 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
4377 function Range(anchor, head) {
4378 this.anchor = anchor; this.head = head;
4382 from: function() { return minPos(this.anchor, this.head); },
4383 to: function() { return maxPos(this.anchor, this.head); },
4385 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
4389 // Take an unsorted, potentially overlapping set of ranges, and
4390 // build a selection out of it. 'Consumes' ranges array (modifying
4392 function normalizeSelection(ranges, primIndex) {
4393 var prim = ranges[primIndex];
4394 ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
4395 primIndex = indexOf(ranges, prim);
4396 for (var i = 1; i < ranges.length; i++) {
4397 var cur = ranges[i], prev = ranges[i - 1];
4398 if (cmp(prev.to(), cur.from()) >= 0) {
4399 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
4400 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
4401 if (i <= primIndex) --primIndex;
4402 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
4405 return new Selection(ranges, primIndex);
4408 function simpleSelection(anchor, head) {
4409 return new Selection([new Range(anchor, head || anchor)], 0);
4412 // Most of the external API clips given positions to make sure they
4413 // actually exist within the document.
4414 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
4415 function clipPos(doc, pos) {
4416 if (pos.line < doc.first) return Pos(doc.first, 0);
4417 var last = doc.first + doc.size - 1;
4418 if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
4419 return clipToLen(pos, getLine(doc, pos.line).text.length);
4421 function clipToLen(pos, linelen) {
4423 if (ch == null || ch > linelen) return Pos(pos.line, linelen);
4424 else if (ch < 0) return Pos(pos.line, 0);
4427 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
4428 function clipPosArray(doc, array) {
4429 for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
4433 // SELECTION UPDATES
4435 // The 'scroll' parameter given to many of these indicated whether
4436 // the new cursor position should be scrolled into view after
4437 // modifying the selection.
4439 // If shift is held or the extend flag is set, extends a range to
4440 // include a given position (and optionally a second position).
4441 // Otherwise, simply returns the range between the given positions.
4442 // Used for cursor motion and such.
4443 function extendRange(doc, range, head, other) {
4444 if (doc.cm && doc.cm.display.shift || doc.extend) {
4445 var anchor = range.anchor;
4447 var posBefore = cmp(head, anchor) < 0;
4448 if (posBefore != (cmp(other, anchor) < 0)) {
4451 } else if (posBefore != (cmp(head, other) < 0)) {
4455 return new Range(anchor, head);
4457 return new Range(other || head, head);
4461 // Extend the primary selection range, discard the rest.
4462 function extendSelection(doc, head, other, options) {
4463 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
4466 // Extend all selections (pos is an array of selections with length
4467 // equal the number of selections)
4468 function extendSelections(doc, heads, options) {
4469 for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
4470 out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
4471 var newSel = normalizeSelection(out, doc.sel.primIndex);
4472 setSelection(doc, newSel, options);
4475 // Updates a single range in the selection.
4476 function replaceOneSelection(doc, i, range, options) {
4477 var ranges = doc.sel.ranges.slice(0);
4479 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
4482 // Reset the selection to a single range.
4483 function setSimpleSelection(doc, anchor, head, options) {
4484 setSelection(doc, simpleSelection(anchor, head), options);
4487 // Give beforeSelectionChange handlers a change to influence a
4488 // selection update.
4489 function filterSelectionChange(doc, sel, options) {
4492 update: function(ranges) {
4494 for (var i = 0; i < ranges.length; i++)
4495 this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
4496 clipPos(doc, ranges[i].head));
4498 origin: options && options.origin
4500 signal(doc, "beforeSelectionChange", doc, obj);
4501 if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
4502 if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
4506 function setSelectionReplaceHistory(doc, sel, options) {
4507 var done = doc.history.done, last = lst(done);
4508 if (last && last.ranges) {
4509 done[done.length - 1] = sel;
4510 setSelectionNoUndo(doc, sel, options);
4512 setSelection(doc, sel, options);
4516 // Set a new selection.
4517 function setSelection(doc, sel, options) {
4518 setSelectionNoUndo(doc, sel, options);
4519 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
4522 function setSelectionNoUndo(doc, sel, options) {
4523 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
4524 sel = filterSelectionChange(doc, sel, options);
4526 var bias = options && options.bias ||
4527 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
4528 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
4530 if (!(options && options.scroll === false) && doc.cm)
4531 ensureCursorVisible(doc.cm);
4534 function setSelectionInner(doc, sel) {
4535 if (sel.equals(doc.sel)) return;
4540 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
4541 signalCursorActivity(doc.cm);
4543 signalLater(doc, "cursorActivity", doc);
4546 // Verify that the selection does not partially select any atomic
4548 function reCheckSelection(doc) {
4549 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
4552 // Return a selection that does not partially select any atomic
4554 function skipAtomicInSelection(doc, sel, bias, mayClear) {
4556 for (var i = 0; i < sel.ranges.length; i++) {
4557 var range = sel.ranges[i];
4558 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
4559 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
4560 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
4561 if (out || newAnchor != range.anchor || newHead != range.head) {
4562 if (!out) out = sel.ranges.slice(0, i);
4563 out[i] = new Range(newAnchor, newHead);
4566 return out ? normalizeSelection(out, sel.primIndex) : sel;
4569 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
4570 var line = getLine(doc, pos.line);
4571 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
4572 var sp = line.markedSpans[i], m = sp.marker;
4573 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
4574 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
4576 signal(m, "beforeCursorEnter");
4577 if (m.explicitlyCleared) {
4578 if (!line.markedSpans) break;
4579 else {--i; continue;}
4582 if (!m.atomic) continue;
4585 var near = m.find(dir < 0 ? 1 : -1), diff;
4586 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
4587 near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null);
4588 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
4589 return skipAtomicInner(doc, near, pos, dir, mayClear);
4592 var far = m.find(dir < 0 ? -1 : 1);
4593 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
4594 far = movePos(doc, far, dir, far.line == pos.line ? line : null);
4595 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null;
4601 // Ensure a given position is not inside an atomic range.
4602 function skipAtomic(doc, pos, oldPos, bias, mayClear) {
4603 var dir = bias || 1;
4604 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
4605 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
4606 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
4607 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
4609 doc.cantEdit = true;
4610 return Pos(doc.first, 0);
4615 function movePos(doc, pos, dir, line) {
4616 if (dir < 0 && pos.ch == 0) {
4617 if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1));
4619 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
4620 if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0);
4623 return new Pos(pos.line, pos.ch + dir);
4627 // SELECTION DRAWING
4629 function updateSelection(cm) {
4630 cm.display.input.showSelection(cm.display.input.prepareSelection());
4633 function prepareSelection(cm, primary) {
4634 var doc = cm.doc, result = {};
4635 var curFragment = result.cursors = document.createDocumentFragment();
4636 var selFragment = result.selection = document.createDocumentFragment();
4638 for (var i = 0; i < doc.sel.ranges.length; i++) {
4639 if (primary === false && i == doc.sel.primIndex) continue;
4640 var range = doc.sel.ranges[i];
4641 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue;
4642 var collapsed = range.empty();
4643 if (collapsed || cm.options.showCursorWhenSelecting)
4644 drawSelectionCursor(cm, range.head, curFragment);
4646 drawSelectionRange(cm, range, selFragment);
4651 // Draws a cursor for the given range
4652 function drawSelectionCursor(cm, head, output) {
4653 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
4655 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
4656 cursor.style.left = pos.left + "px";
4657 cursor.style.top = pos.top + "px";
4658 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
4661 // Secondary cursor, shown when on a 'jump' in bi-directional text
4662 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
4663 otherCursor.style.display = "";
4664 otherCursor.style.left = pos.other.left + "px";
4665 otherCursor.style.top = pos.other.top + "px";
4666 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
4670 // Draws the given range as a highlighted selection
4671 function drawSelectionRange(cm, range, output) {
4672 var display = cm.display, doc = cm.doc;
4673 var fragment = document.createDocumentFragment();
4674 var padding = paddingH(cm.display), leftSide = padding.left;
4675 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
4677 function add(left, top, width, bottom) {
4678 if (top < 0) top = 0;
4679 top = Math.round(top);
4680 bottom = Math.round(bottom);
4681 fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
4682 "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
4683 "px; height: " + (bottom - top) + "px"));
4686 function drawForLine(line, fromArg, toArg) {
4687 var lineObj = getLine(doc, line);
4688 var lineLen = lineObj.text.length;
4690 function coords(ch, bias) {
4691 return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
4694 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
4695 var leftPos = coords(from, "left"), rightPos, left, right;
4698 left = right = leftPos.left;
4700 rightPos = coords(to - 1, "right");
4701 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
4702 left = leftPos.left;
4703 right = rightPos.right;
4705 if (fromArg == null && from == 0) left = leftSide;
4706 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
4707 add(left, leftPos.top, null, leftPos.bottom);
4709 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
4711 if (toArg == null && to == lineLen) right = rightSide;
4712 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
4714 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
4716 if (left < leftSide + 1) left = leftSide;
4717 add(left, rightPos.top, right - left, rightPos.bottom);
4719 return {start: start, end: end};
4722 var sFrom = range.from(), sTo = range.to();
4723 if (sFrom.line == sTo.line) {
4724 drawForLine(sFrom.line, sFrom.ch, sTo.ch);
4726 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
4727 var singleVLine = visualLine(fromLine) == visualLine(toLine);
4728 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
4729 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
4731 if (leftEnd.top < rightStart.top - 2) {
4732 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
4733 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
4735 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
4738 if (leftEnd.bottom < rightStart.top)
4739 add(leftSide, leftEnd.bottom, null, rightStart.top);
4742 output.appendChild(fragment);
4746 function restartBlink(cm) {
4747 if (!cm.state.focused) return;
4748 var display = cm.display;
4749 clearInterval(display.blinker);
4751 display.cursorDiv.style.visibility = "";
4752 if (cm.options.cursorBlinkRate > 0)
4753 display.blinker = setInterval(function() {
4754 display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
4755 }, cm.options.cursorBlinkRate);
4756 else if (cm.options.cursorBlinkRate < 0)
4757 display.cursorDiv.style.visibility = "hidden";
4762 function startWorker(cm, time) {
4763 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
4764 cm.state.highlight.set(time, bind(highlightWorker, cm));
4767 function highlightWorker(cm) {
4769 if (doc.frontier < doc.first) doc.frontier = doc.first;
4770 if (doc.frontier >= cm.display.viewTo) return;
4771 var end = +new Date + cm.options.workTime;
4772 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
4773 var changedLines = [];
4775 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
4776 if (doc.frontier >= cm.display.viewFrom) { // Visible
4777 var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength;
4778 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true);
4779 line.styles = highlighted.styles;
4780 var oldCls = line.styleClasses, newCls = highlighted.classes;
4781 if (newCls) line.styleClasses = newCls;
4782 else if (oldCls) line.styleClasses = null;
4783 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
4784 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
4785 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
4786 if (ischange) changedLines.push(doc.frontier);
4787 line.stateAfter = tooLong ? state : copyState(doc.mode, state);
4789 if (line.text.length <= cm.options.maxHighlightLength)
4790 processLine(cm, line.text, state);
4791 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
4794 if (+new Date > end) {
4795 startWorker(cm, cm.options.workDelay);
4799 if (changedLines.length) runInOp(cm, function() {
4800 for (var i = 0; i < changedLines.length; i++)
4801 regLineChange(cm, changedLines[i], "text");
4805 // Finds the line to start with when starting a parse. Tries to
4806 // find a line with a stateAfter, so that it can start with a
4807 // valid state. If that fails, it returns the line with the
4808 // smallest indentation, which tends to need the least context to
4810 function findStartLine(cm, n, precise) {
4811 var minindent, minline, doc = cm.doc;
4812 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
4813 for (var search = n; search > lim; --search) {
4814 if (search <= doc.first) return doc.first;
4815 var line = getLine(doc, search - 1);
4816 if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
4817 var indented = countColumn(line.text, null, cm.options.tabSize);
4818 if (minline == null || minindent > indented) {
4819 minline = search - 1;
4820 minindent = indented;
4826 function getStateBefore(cm, n, precise) {
4827 var doc = cm.doc, display = cm.display;
4828 if (!doc.mode.startState) return true;
4829 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
4830 if (!state) state = startState(doc.mode);
4831 else state = copyState(doc.mode, state);
4832 doc.iter(pos, n, function(line) {
4833 processLine(cm, line.text, state);
4834 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
4835 line.stateAfter = save ? copyState(doc.mode, state) : null;
4838 if (precise) doc.frontier = pos;
4842 // POSITION MEASUREMENT
4844 function paddingTop(display) {return display.lineSpace.offsetTop;}
4845 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
4846 function paddingH(display) {
4847 if (display.cachedPaddingH) return display.cachedPaddingH;
4848 var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
4849 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
4850 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
4851 if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
4855 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
4856 function displayWidth(cm) {
4857 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
4859 function displayHeight(cm) {
4860 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
4863 // Ensure the lineView.wrapping.heights array is populated. This is
4864 // an array of bottom offsets for the lines that make up a drawn
4865 // line. When lineWrapping is on, there might be more than one
4867 function ensureLineHeights(cm, lineView, rect) {
4868 var wrapping = cm.options.lineWrapping;
4869 var curWidth = wrapping && displayWidth(cm);
4870 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
4871 var heights = lineView.measure.heights = [];
4873 lineView.measure.width = curWidth;
4874 var rects = lineView.text.firstChild.getClientRects();
4875 for (var i = 0; i < rects.length - 1; i++) {
4876 var cur = rects[i], next = rects[i + 1];
4877 if (Math.abs(cur.bottom - next.bottom) > 2)
4878 heights.push((cur.bottom + next.top) / 2 - rect.top);
4881 heights.push(rect.bottom - rect.top);
4885 // Find a line map (mapping character offsets to text nodes) and a
4886 // measurement cache for the given line number. (A line view might
4887 // contain multiple lines when collapsed ranges are present.)
4888 function mapFromLineView(lineView, line, lineN) {
4889 if (lineView.line == line)
4890 return {map: lineView.measure.map, cache: lineView.measure.cache};
4891 for (var i = 0; i < lineView.rest.length; i++)
4892 if (lineView.rest[i] == line)
4893 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
4894 for (var i = 0; i < lineView.rest.length; i++)
4895 if (lineNo(lineView.rest[i]) > lineN)
4896 return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
4899 // Render a line into the hidden node display.externalMeasured. Used
4900 // when measurement is needed for a line that's not in the viewport.
4901 function updateExternalMeasurement(cm, line) {
4902 line = visualLine(line);
4903 var lineN = lineNo(line);
4904 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
4906 var built = view.built = buildLineContent(cm, view);
4907 view.text = built.pre;
4908 removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
4912 // Get a {top, bottom, left, right} box (in line-local coordinates)
4913 // for a given character.
4914 function measureChar(cm, line, ch, bias) {
4915 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
4918 // Find a line view that corresponds to the given line number.
4919 function findViewForLine(cm, lineN) {
4920 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
4921 return cm.display.view[findViewIndex(cm, lineN)];
4922 var ext = cm.display.externalMeasured;
4923 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
4927 // Measurement can be split in two steps, the set-up work that
4928 // applies to the whole line, and the measurement of the actual
4929 // character. Functions like coordsChar, that need to do a lot of
4930 // measurements in a row, can thus ensure that the set-up work is
4932 function prepareMeasureForLine(cm, line) {
4933 var lineN = lineNo(line);
4934 var view = findViewForLine(cm, lineN);
4935 if (view && !view.text) {
4937 } else if (view && view.changes) {
4938 updateLineForChanges(cm, view, lineN, getDimensions(cm));
4939 cm.curOp.forceUpdate = true;
4942 view = updateExternalMeasurement(cm, line);
4944 var info = mapFromLineView(view, line, lineN);
4946 line: line, view: view, rect: null,
4947 map: info.map, cache: info.cache, before: info.before,
4952 // Given a prepared measurement object, measures the position of an
4953 // actual character (or fetches it from the cache).
4954 function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
4955 if (prepared.before) ch = -1;
4956 var key = ch + (bias || ""), found;
4957 if (prepared.cache.hasOwnProperty(key)) {
4958 found = prepared.cache[key];
4961 prepared.rect = prepared.view.text.getBoundingClientRect();
4962 if (!prepared.hasHeights) {
4963 ensureLineHeights(cm, prepared.view, prepared.rect);
4964 prepared.hasHeights = true;
4966 found = measureCharInner(cm, prepared, ch, bias);
4967 if (!found.bogus) prepared.cache[key] = found;
4969 return {left: found.left, right: found.right,
4970 top: varHeight ? found.rtop : found.top,
4971 bottom: varHeight ? found.rbottom : found.bottom};
4974 var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
4976 function nodeAndOffsetInLineMap(map, ch, bias) {
4977 var node, start, end, collapse;
4978 // First, search the line map for the text node corresponding to,
4979 // or closest to, the target character.
4980 for (var i = 0; i < map.length; i += 3) {
4981 var mStart = map[i], mEnd = map[i + 1];
4985 } else if (ch < mEnd) {
4986 start = ch - mStart;
4988 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
4989 end = mEnd - mStart;
4991 if (ch >= mEnd) collapse = "right";
4993 if (start != null) {
4995 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
4997 if (bias == "left" && start == 0)
4998 while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
4999 node = map[(i -= 3) + 2];
5002 if (bias == "right" && start == mEnd - mStart)
5003 while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
5004 node = map[(i += 3) + 2];
5010 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
5013 function measureCharInner(cm, prepared, ch, bias) {
5014 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
5015 var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
5018 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
5019 for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
5020 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
5021 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
5022 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
5023 rect = node.parentNode.getBoundingClientRect();
5024 } else if (ie && cm.options.lineWrapping) {
5025 var rects = range(node, start, end).getClientRects();
5027 rect = rects[bias == "right" ? rects.length - 1 : 0];
5031 rect = range(node, start, end).getBoundingClientRect() || nullRect;
5033 if (rect.left || rect.right || start == 0) break;
5038 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
5039 } else { // If it is a widget, simply get the box for the whole widget.
5040 if (start > 0) collapse = bias = "right";
5042 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
5043 rect = rects[bias == "right" ? rects.length - 1 : 0];
5045 rect = node.getBoundingClientRect();
5047 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
5048 var rSpan = node.parentNode.getClientRects()[0];
5050 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
5055 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
5056 var mid = (rtop + rbot) / 2;
5057 var heights = prepared.view.measure.heights;
5058 for (var i = 0; i < heights.length - 1; i++)
5059 if (mid < heights[i]) break;
5060 var top = i ? heights[i - 1] : 0, bot = heights[i];
5061 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
5062 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
5063 top: top, bottom: bot};
5064 if (!rect.left && !rect.right) result.bogus = true;
5065 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
5070 // Work around problem with bounding client rects on ranges being
5071 // returned incorrectly when zoomed on IE10 and below.
5072 function maybeUpdateRectForZooming(measure, rect) {
5073 if (!window.screen || screen.logicalXDPI == null ||
5074 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
5076 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
5077 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
5078 return {left: rect.left * scaleX, right: rect.right * scaleX,
5079 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
5082 function clearLineMeasurementCacheFor(lineView) {
5083 if (lineView.measure) {
5084 lineView.measure.cache = {};
5085 lineView.measure.heights = null;
5086 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
5087 lineView.measure.caches[i] = {};
5091 function clearLineMeasurementCache(cm) {
5092 cm.display.externalMeasure = null;
5093 removeChildren(cm.display.lineMeasure);
5094 for (var i = 0; i < cm.display.view.length; i++)
5095 clearLineMeasurementCacheFor(cm.display.view[i]);
5098 function clearCaches(cm) {
5099 clearLineMeasurementCache(cm);
5100 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
5101 if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
5102 cm.display.lineNumChars = null;
5105 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
5106 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
5108 // Converts a {top, bottom, left, right} box from line-local
5109 // coordinates into another coordinate system. Context may be one of
5110 // "line", "div" (display.lineDiv), "local"/null (editor), "window",
5112 function intoCoordSystem(cm, lineObj, rect, context) {
5113 if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
5114 var size = widgetHeight(lineObj.widgets[i]);
5115 rect.top += size; rect.bottom += size;
5117 if (context == "line") return rect;
5118 if (!context) context = "local";
5119 var yOff = heightAtLine(lineObj);
5120 if (context == "local") yOff += paddingTop(cm.display);
5121 else yOff -= cm.display.viewOffset;
5122 if (context == "page" || context == "window") {
5123 var lOff = cm.display.lineSpace.getBoundingClientRect();
5124 yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
5125 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
5126 rect.left += xOff; rect.right += xOff;
5128 rect.top += yOff; rect.bottom += yOff;
5132 // Coverts a box from "div" coords to another coordinate system.
5133 // Context may be "window", "page", "div", or "local"/null.
5134 function fromCoordSystem(cm, coords, context) {
5135 if (context == "div") return coords;
5136 var left = coords.left, top = coords.top;
5137 // First move into "page" coordinate system
5138 if (context == "page") {
5139 left -= pageScrollX();
5140 top -= pageScrollY();
5141 } else if (context == "local" || !context) {
5142 var localBox = cm.display.sizer.getBoundingClientRect();
5143 left += localBox.left;
5144 top += localBox.top;
5147 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
5148 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
5151 function charCoords(cm, pos, context, lineObj, bias) {
5152 if (!lineObj) lineObj = getLine(cm.doc, pos.line);
5153 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
5156 // Returns a box for a given cursor position, which may have an
5157 // 'other' property containing the position of the secondary cursor
5158 // on a bidi boundary.
5159 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
5160 lineObj = lineObj || getLine(cm.doc, pos.line);
5161 if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
5162 function get(ch, right) {
5163 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
5164 if (right) m.left = m.right; else m.right = m.left;
5165 return intoCoordSystem(cm, lineObj, m, context);
5167 function getBidi(ch, partPos) {
5168 var part = order[partPos], right = part.level % 2;
5169 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
5170 part = order[--partPos];
5171 ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
5173 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
5174 part = order[++partPos];
5175 ch = bidiLeft(part) - part.level % 2;
5178 if (right && ch == part.to && ch > part.from) return get(ch - 1);
5179 return get(ch, right);
5181 var order = getOrder(lineObj), ch = pos.ch;
5182 if (!order) return get(ch);
5183 var partPos = getBidiPartAt(order, ch);
5184 var val = getBidi(ch, partPos);
5185 if (bidiOther != null) val.other = getBidi(ch, bidiOther);
5189 // Used to cheaply estimate the coordinates for a position. Used for
5190 // intermediate scroll updates.
5191 function estimateCoords(cm, pos) {
5192 var left = 0, pos = clipPos(cm.doc, pos);
5193 if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
5194 var lineObj = getLine(cm.doc, pos.line);
5195 var top = heightAtLine(lineObj) + paddingTop(cm.display);
5196 return {left: left, right: left, top: top, bottom: top + lineObj.height};
5199 // Positions returned by coordsChar contain some extra information.
5200 // xRel is the relative x position of the input coordinates compared
5201 // to the found position (so xRel > 0 means the coordinates are to
5202 // the right of the character position, for example). When outside
5203 // is true, that means the coordinates lie outside the line's
5205 function PosWithInfo(line, ch, outside, xRel) {
5206 var pos = Pos(line, ch);
5208 if (outside) pos.outside = true;
5212 // Compute the character position closest to the given coordinates.
5213 // Input must be lineSpace-local ("div" coordinate system).
5214 function coordsChar(cm, x, y) {
5216 y += cm.display.viewOffset;
5217 if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
5218 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
5220 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
5223 var lineObj = getLine(doc, lineN);
5225 var found = coordsCharInner(cm, lineObj, lineN, x, y);
5226 var merged = collapsedSpanAtEnd(lineObj);
5227 var mergedPos = merged && merged.find(0, true);
5228 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
5229 lineN = lineNo(lineObj = mergedPos.to.line);
5235 function coordsCharInner(cm, lineObj, lineNo, x, y) {
5236 var innerOff = y - heightAtLine(lineObj);
5237 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
5238 var preparedMeasure = prepareMeasureForLine(cm, lineObj);
5241 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
5243 if (innerOff > sp.bottom) return sp.left - adjust;
5244 else if (innerOff < sp.top) return sp.left + adjust;
5245 else wrongLine = false;
5249 var bidi = getOrder(lineObj), dist = lineObj.text.length;
5250 var from = lineLeft(lineObj), to = lineRight(lineObj);
5251 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
5253 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
5254 // Do a binary search between these bounds.
5256 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
5257 var ch = x < fromX || x - fromX <= toX - x ? from : to;
5258 var xDiff = x - (ch == from ? fromX : toX);
5259 while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
5260 var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
5261 xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
5264 var step = Math.ceil(dist / 2), middle = from + step;
5267 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
5269 var middleX = getX(middle);
5270 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
5271 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
5276 // Compute the default text height.
5277 function textHeight(display) {
5278 if (display.cachedTextHeight != null) return display.cachedTextHeight;
5279 if (measureText == null) {
5280 measureText = elt("pre");
5281 // Measure a bunch of lines, for browsers that compute
5282 // fractional heights.
5283 for (var i = 0; i < 49; ++i) {
5284 measureText.appendChild(document.createTextNode("x"));
5285 measureText.appendChild(elt("br"));
5287 measureText.appendChild(document.createTextNode("x"));
5289 removeChildrenAndAdd(display.measure, measureText);
5290 var height = measureText.offsetHeight / 50;
5291 if (height > 3) display.cachedTextHeight = height;
5292 removeChildren(display.measure);
5296 // Compute the default character width.
5297 function charWidth(display) {
5298 if (display.cachedCharWidth != null) return display.cachedCharWidth;
5299 var anchor = elt("span", "xxxxxxxxxx");
5300 var pre = elt("pre", [anchor]);
5301 removeChildrenAndAdd(display.measure, pre);
5302 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
5303 if (width > 2) display.cachedCharWidth = width;
5309 // Operations are used to wrap a series of changes to the editor
5310 // state in such a way that each change won't have to update the
5311 // cursor and display (which would be awkward, slow, and
5312 // error-prone). Instead, display updates are batched and then all
5313 // combined and executed at once.
5315 var operationGroup = null;
5318 // Start a new operation.
5319 function startOperation(cm) {
5322 viewChanged: false, // Flag that indicates that lines might need to be redrawn
5323 startHeight: cm.doc.height, // Used to detect need to update scrollbar
5324 forceUpdate: false, // Used to force a redraw
5325 updateInput: null, // Whether to reset the input textarea
5326 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
5327 changeObjs: null, // Accumulated changes, for firing change events
5328 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
5329 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
5330 selectionChanged: false, // Whether the selection needs to be redrawn
5331 updateMaxLine: false, // Set when the widest line needs to be determined anew
5332 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
5333 scrollToPos: null, // Used to scroll to a specific position
5335 id: ++nextOpId // Unique ID
5337 if (operationGroup) {
5338 operationGroup.ops.push(cm.curOp);
5340 cm.curOp.ownsGroup = operationGroup = {
5342 delayedCallbacks: []
5347 function fireCallbacksForOps(group) {
5348 // Calls delayed callbacks and cursorActivity handlers until no
5350 var callbacks = group.delayedCallbacks, i = 0;
5352 for (; i < callbacks.length; i++)
5353 callbacks[i].call(null);
5354 for (var j = 0; j < group.ops.length; j++) {
5355 var op = group.ops[j];
5356 if (op.cursorActivityHandlers)
5357 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
5358 op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm);
5360 } while (i < callbacks.length);
5363 // Finish an operation, updating the display and signalling delayed events
5364 function endOperation(cm) {
5365 var op = cm.curOp, group = op.ownsGroup;
5368 try { fireCallbacksForOps(group); }
5370 operationGroup = null;
5371 for (var i = 0; i < group.ops.length; i++)
5372 group.ops[i].cm.curOp = null;
5373 endOperations(group);
5377 // The DOM updates done when an operation finishes are batched so
5378 // that the minimum number of relayouts are required.
5379 function endOperations(group) {
5380 var ops = group.ops;
5381 for (var i = 0; i < ops.length; i++) // Read DOM
5382 endOperation_R1(ops[i]);
5383 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
5384 endOperation_W1(ops[i]);
5385 for (var i = 0; i < ops.length; i++) // Read DOM
5386 endOperation_R2(ops[i]);
5387 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
5388 endOperation_W2(ops[i]);
5389 for (var i = 0; i < ops.length; i++) // Read DOM
5390 endOperation_finish(ops[i]);
5393 function endOperation_R1(op) {
5394 var cm = op.cm, display = cm.display;
5395 maybeClipScrollbars(cm);
5396 if (op.updateMaxLine) findMaxLine(cm);
5398 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
5399 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
5400 op.scrollToPos.to.line >= display.viewTo) ||
5401 display.maxLineChanged && cm.options.lineWrapping;
5402 op.update = op.mustUpdate &&
5403 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
5406 function endOperation_W1(op) {
5407 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
5410 function endOperation_R2(op) {
5411 var cm = op.cm, display = cm.display;
5412 if (op.updatedDisplay) updateHeightsInViewport(cm);
5414 op.barMeasure = measureForScrollbars(cm);
5416 // If the max line changed since it was last measured, measure it,
5417 // and ensure the document's width matches it.
5418 // updateDisplay_W2 will use these properties to do the actual resizing
5419 if (display.maxLineChanged && !cm.options.lineWrapping) {
5420 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
5421 cm.display.sizerWidth = op.adjustWidthTo;
5422 op.barMeasure.scrollWidth =
5423 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
5424 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
5427 if (op.updatedDisplay || op.selectionChanged)
5428 op.preparedSelection = display.input.prepareSelection(op.focus);
5431 function endOperation_W2(op) {
5434 if (op.adjustWidthTo != null) {
5435 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
5436 if (op.maxScrollLeft < cm.doc.scrollLeft)
5437 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
5438 cm.display.maxLineChanged = false;
5441 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
5442 if (op.preparedSelection)
5443 cm.display.input.showSelection(op.preparedSelection, takeFocus);
5444 if (op.updatedDisplay || op.startHeight != cm.doc.height)
5445 updateScrollbars(cm, op.barMeasure);
5446 if (op.updatedDisplay)
5447 setDocumentHeight(cm, op.barMeasure);
5449 if (op.selectionChanged) restartBlink(cm);
5451 if (cm.state.focused && op.updateInput)
5452 cm.display.input.reset(op.typing);
5453 if (takeFocus) ensureFocus(op.cm);
5456 function endOperation_finish(op) {
5457 var cm = op.cm, display = cm.display, doc = cm.doc;
5459 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
5461 // Abort mouse wheel delta measurement, when scrolling explicitly
5462 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
5463 display.wheelStartX = display.wheelStartY = null;
5465 // Propagate the scroll position to the actual DOM scroller
5466 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
5467 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
5468 display.scrollbars.setScrollTop(doc.scrollTop);
5469 display.scroller.scrollTop = doc.scrollTop;
5471 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
5472 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
5473 display.scrollbars.setScrollLeft(doc.scrollLeft);
5474 display.scroller.scrollLeft = doc.scrollLeft;
5475 alignHorizontally(cm);
5477 // If we need to scroll a specific position into view, do so.
5478 if (op.scrollToPos) {
5479 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
5480 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
5481 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
5484 // Fire events for markers that are hidden/unidden by editing or
5486 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
5487 if (hidden) for (var i = 0; i < hidden.length; ++i)
5488 if (!hidden[i].lines.length) signal(hidden[i], "hide");
5489 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
5490 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
5492 if (display.wrapper.offsetHeight)
5493 doc.scrollTop = cm.display.scroller.scrollTop;
5495 // Fire change events, and delayed event handlers
5497 signal(cm, "changes", cm, op.changeObjs);
5502 // Run the given function in an operation
5503 function runInOp(cm, f) {
5504 if (cm.curOp) return f();
5507 finally { endOperation(cm); }
5509 // Wraps a function in an operation. Returns the wrapped function.
5510 function operation(cm, f) {
5512 if (cm.curOp) return f.apply(cm, arguments);
5514 try { return f.apply(cm, arguments); }
5515 finally { endOperation(cm); }
5518 // Used to add methods to editor and doc instances, wrapping them in
5520 function methodOp(f) {
5522 if (this.curOp) return f.apply(this, arguments);
5523 startOperation(this);
5524 try { return f.apply(this, arguments); }
5525 finally { endOperation(this); }
5528 function docMethodOp(f) {
5531 if (!cm || cm.curOp) return f.apply(this, arguments);
5533 try { return f.apply(this, arguments); }
5534 finally { endOperation(cm); }
5540 // These objects are used to represent the visible (currently drawn)
5541 // part of the document. A LineView may correspond to multiple
5542 // logical lines, if those are connected by collapsed ranges.
5543 function LineView(doc, line, lineN) {
5544 // The starting line
5546 // Continuing lines, if any
5547 this.rest = visualLineContinued(line);
5548 // Number of logical lines in this visual line
5549 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
5550 this.node = this.text = null;
5551 this.hidden = lineIsHidden(doc, line);
5554 // Create a range of LineView objects for the given lines.
5555 function buildViewArray(cm, from, to) {
5556 var array = [], nextPos;
5557 for (var pos = from; pos < to; pos = nextPos) {
5558 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
5559 nextPos = pos + view.size;
5565 // Updates the display.view data structure for a given change to the
5566 // document. From and to are in pre-change coordinates. Lendiff is
5567 // the amount of lines added or subtracted by the change. This is
5568 // used for changes that span multiple lines, or change the way
5569 // lines are divided into visual lines. regLineChange (below)
5570 // registers single-line changes.
5571 function regChange(cm, from, to, lendiff) {
5572 if (from == null) from = cm.doc.first;
5573 if (to == null) to = cm.doc.first + cm.doc.size;
5574 if (!lendiff) lendiff = 0;
5576 var display = cm.display;
5577 if (lendiff && to < display.viewTo &&
5578 (display.updateLineNumbers == null || display.updateLineNumbers > from))
5579 display.updateLineNumbers = from;
5581 cm.curOp.viewChanged = true;
5583 if (from >= display.viewTo) { // Change after
5584 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
5586 } else if (to <= display.viewFrom) { // Change before
5587 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
5590 display.viewFrom += lendiff;
5591 display.viewTo += lendiff;
5593 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
5595 } else if (from <= display.viewFrom) { // Top overlap
5596 var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
5598 display.view = display.view.slice(cut.index);
5599 display.viewFrom = cut.lineN;
5600 display.viewTo += lendiff;
5604 } else if (to >= display.viewTo) { // Bottom overlap
5605 var cut = viewCuttingPoint(cm, from, from, -1);
5607 display.view = display.view.slice(0, cut.index);
5608 display.viewTo = cut.lineN;
5612 } else { // Gap in the middle
5613 var cutTop = viewCuttingPoint(cm, from, from, -1);
5614 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
5615 if (cutTop && cutBot) {
5616 display.view = display.view.slice(0, cutTop.index)
5617 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
5618 .concat(display.view.slice(cutBot.index));
5619 display.viewTo += lendiff;
5625 var ext = display.externalMeasured;
5628 ext.lineN += lendiff;
5629 else if (from < ext.lineN + ext.size)
5630 display.externalMeasured = null;
5634 // Register a change to a single line. Type must be one of "text",
5635 // "gutter", "class", "widget"
5636 function regLineChange(cm, line, type) {
5637 cm.curOp.viewChanged = true;
5638 var display = cm.display, ext = cm.display.externalMeasured;
5639 if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
5640 display.externalMeasured = null;
5642 if (line < display.viewFrom || line >= display.viewTo) return;
5643 var lineView = display.view[findViewIndex(cm, line)];
5644 if (lineView.node == null) return;
5645 var arr = lineView.changes || (lineView.changes = []);
5646 if (indexOf(arr, type) == -1) arr.push(type);
5650 function resetView(cm) {
5651 cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
5652 cm.display.view = [];
5653 cm.display.viewOffset = 0;
5656 // Find the view element corresponding to a given line. Return null
5657 // when the line isn't visible.
5658 function findViewIndex(cm, n) {
5659 if (n >= cm.display.viewTo) return null;
5660 n -= cm.display.viewFrom;
5661 if (n < 0) return null;
5662 var view = cm.display.view;
5663 for (var i = 0; i < view.length; i++) {
5665 if (n < 0) return i;
5669 function viewCuttingPoint(cm, oldN, newN, dir) {
5670 var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
5671 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
5672 return {index: index, lineN: newN};
5673 for (var i = 0, n = cm.display.viewFrom; i < index; i++)
5677 if (index == view.length - 1) return null;
5678 diff = (n + view[index].size) - oldN;
5683 oldN += diff; newN += diff;
5685 while (visualLineNo(cm.doc, newN) != newN) {
5686 if (index == (dir < 0 ? 0 : view.length - 1)) return null;
5687 newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
5690 return {index: index, lineN: newN};
5693 // Force the view to cover a given range, adding empty view element
5694 // or clipping off existing ones as needed.
5695 function adjustView(cm, from, to) {
5696 var display = cm.display, view = display.view;
5697 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
5698 display.view = buildViewArray(cm, from, to);
5699 display.viewFrom = from;
5701 if (display.viewFrom > from)
5702 display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
5703 else if (display.viewFrom < from)
5704 display.view = display.view.slice(findViewIndex(cm, from));
5705 display.viewFrom = from;
5706 if (display.viewTo < to)
5707 display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
5708 else if (display.viewTo > to)
5709 display.view = display.view.slice(0, findViewIndex(cm, to));
5711 display.viewTo = to;
5714 // Count the number of lines in the view whose DOM representation is
5715 // out of date (or nonexistent).
5716 function countDirtyView(cm) {
5717 var view = cm.display.view, dirty = 0;
5718 for (var i = 0; i < view.length; i++) {
5719 var lineView = view[i];
5720 if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
5727 // Attach the necessary event handlers when initializing the editor
5728 function registerEventHandlers(cm) {
5730 on(d.scroller, "mousedown", operation(cm, onMouseDown));
5731 // Older IE's will not fire a second mousedown for a double click
5732 if (ie && ie_version < 11)
5733 on(d.scroller, "dblclick", operation(cm, function(e) {
5734 if (signalDOMEvent(cm, e)) return;
5735 var pos = posFromMouse(cm, e);
5736 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
5737 e_preventDefault(e);
5738 var word = cm.findWordAt(pos);
5739 extendSelection(cm.doc, word.anchor, word.head);
5742 on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
5743 // Some browsers fire contextmenu *after* opening the menu, at
5744 // which point we can't mess with it anymore. Context menu is
5745 // handled in onMouseDown for these browsers.
5746 if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
5748 // Used to suppress mouse event handling when a touch happens
5749 var touchFinished, prevTouch = {end: 0};
5750 function finishTouch() {
5751 if (d.activeTouch) {
5752 touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
5753 prevTouch = d.activeTouch;
5754 prevTouch.end = +new Date;
5757 function isMouseLikeTouchEvent(e) {
5758 if (e.touches.length != 1) return false;
5759 var touch = e.touches[0];
5760 return touch.radiusX <= 1 && touch.radiusY <= 1;
5762 function farAway(touch, other) {
5763 if (other.left == null) return true;
5764 var dx = other.left - touch.left, dy = other.top - touch.top;
5765 return dx * dx + dy * dy > 20 * 20;
5767 on(d.scroller, "touchstart", function(e) {
5768 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
5769 clearTimeout(touchFinished);
5770 var now = +new Date;
5771 d.activeTouch = {start: now, moved: false,
5772 prev: now - prevTouch.end <= 300 ? prevTouch : null};
5773 if (e.touches.length == 1) {
5774 d.activeTouch.left = e.touches[0].pageX;
5775 d.activeTouch.top = e.touches[0].pageY;
5779 on(d.scroller, "touchmove", function() {
5780 if (d.activeTouch) d.activeTouch.moved = true;
5782 on(d.scroller, "touchend", function(e) {
5783 var touch = d.activeTouch;
5784 if (touch && !eventInWidget(d, e) && touch.left != null &&
5785 !touch.moved && new Date - touch.start < 300) {
5786 var pos = cm.coordsChar(d.activeTouch, "page"), range;
5787 if (!touch.prev || farAway(touch, touch.prev)) // Single tap
5788 range = new Range(pos, pos);
5789 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
5790 range = cm.findWordAt(pos);
5792 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
5793 cm.setSelection(range.anchor, range.head);
5795 e_preventDefault(e);
5799 on(d.scroller, "touchcancel", finishTouch);
5801 // Sync scrolling between fake scrollbars and real scrollable
5802 // area, ensure viewport is updated when scrolling.
5803 on(d.scroller, "scroll", function() {
5804 if (d.scroller.clientHeight) {
5805 setScrollTop(cm, d.scroller.scrollTop);
5806 setScrollLeft(cm, d.scroller.scrollLeft, true);
5807 signal(cm, "scroll", cm);
5811 // Listen to wheel events in order to try and update the viewport on time.
5812 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
5813 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
5815 // Prevent wrapper from ever scrolling
5816 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
5819 enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
5820 over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
5821 start: function(e){onDragStart(cm, e);},
5822 drop: operation(cm, onDrop),
5823 leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
5826 var inp = d.input.getField();
5827 on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
5828 on(inp, "keydown", operation(cm, onKeyDown));
5829 on(inp, "keypress", operation(cm, onKeyPress));
5830 on(inp, "focus", bind(onFocus, cm));
5831 on(inp, "blur", bind(onBlur, cm));
5834 function dragDropChanged(cm, value, old) {
5835 var wasOn = old && old != CodeMirror.Init;
5836 if (!value != !wasOn) {
5837 var funcs = cm.display.dragFunctions;
5838 var toggle = value ? on : off;
5839 toggle(cm.display.scroller, "dragstart", funcs.start);
5840 toggle(cm.display.scroller, "dragenter", funcs.enter);
5841 toggle(cm.display.scroller, "dragover", funcs.over);
5842 toggle(cm.display.scroller, "dragleave", funcs.leave);
5843 toggle(cm.display.scroller, "drop", funcs.drop);
5847 // Called when the window resizes
5848 function onResize(cm) {
5850 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
5852 // Might be a text scaling operation, clear size caches.
5853 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
5854 d.scrollbarsClipped = false;
5860 // Return true when the given mouse event happened in a widget
5861 function eventInWidget(display, e) {
5862 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
5863 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
5864 (n.parentNode == display.sizer && n != display.mover))
5869 // Given a mouse event, find the corresponding position. If liberal
5870 // is false, it checks whether a gutter or scrollbar was clicked,
5871 // and returns null if it was. forRect is used by rectangular
5872 // selections, and tries to estimate a character position even for
5873 // coordinates beyond the right of the text.
5874 function posFromMouse(cm, e, liberal, forRect) {
5875 var display = cm.display;
5876 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;
5878 var x, y, space = display.lineSpace.getBoundingClientRect();
5879 // Fails unpredictably on IE[67] when mouse is dragged around quickly.
5880 try { x = e.clientX - space.left; y = e.clientY - space.top; }
5881 catch (e) { return null; }
5882 var coords = coordsChar(cm, x, y), line;
5883 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
5884 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
5885 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
5890 // A mouse down can be a single click, double click, triple click,
5891 // start of selection drag, start of text drag, new cursor
5892 // (ctrl-click), rectangle drag (alt-drag), or xwin
5893 // middle-click-paste. Or it might be a click on something we should
5894 // not interfere with, such as a scrollbar or widget.
5895 function onMouseDown(e) {
5896 var cm = this, display = cm.display;
5897 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return;
5898 display.shift = e.shiftKey;
5900 if (eventInWidget(display, e)) {
5902 // Briefly turn off draggability, to allow widgets to do
5903 // normal dragging things.
5904 display.scroller.draggable = false;
5905 setTimeout(function(){display.scroller.draggable = true;}, 100);
5909 if (clickInGutter(cm, e)) return;
5910 var start = posFromMouse(cm, e);
5913 switch (e_button(e)) {
5915 // #3261: make sure, that we're not starting a second selection
5916 if (cm.state.selectingText)
5917 cm.state.selectingText(e);
5919 leftButtonDown(cm, e, start);
5920 else if (e_target(e) == display.scroller)
5921 e_preventDefault(e);
5924 if (webkit) cm.state.lastMiddleDown = +new Date;
5925 if (start) extendSelection(cm.doc, start);
5926 setTimeout(function() {display.input.focus();}, 20);
5927 e_preventDefault(e);
5930 if (captureRightClick) onContextMenu(cm, e);
5931 else delayBlurEvent(cm);
5936 var lastClick, lastDoubleClick;
5937 function leftButtonDown(cm, e, start) {
5938 if (ie) setTimeout(bind(ensureFocus, cm), 0);
5939 else cm.curOp.focus = activeElt();
5941 var now = +new Date, type;
5942 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) {
5944 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) {
5946 lastDoubleClick = {time: now, pos: start};
5949 lastClick = {time: now, pos: start};
5952 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
5953 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
5954 type == "single" && (contained = sel.contains(start)) > -1 &&
5955 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
5956 (cmp(contained.to(), start) > 0 || start.xRel < 0))
5957 leftButtonStartDrag(cm, e, start, modifier);
5959 leftButtonSelect(cm, e, start, type, modifier);
5962 // Start a text drag. When it ends, see if any dragging actually
5963 // happen, and treat as a click if it didn't.
5964 function leftButtonStartDrag(cm, e, start, modifier) {
5965 var display = cm.display, startTime = +new Date;
5966 var dragEnd = operation(cm, function(e2) {
5967 if (webkit) display.scroller.draggable = false;
5968 cm.state.draggingText = false;
5969 off(document, "mouseup", dragEnd);
5970 off(display.scroller, "drop", dragEnd);
5971 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
5972 e_preventDefault(e2);
5973 if (!modifier && +new Date - 200 < startTime)
5974 extendSelection(cm.doc, start);
5975 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
5976 if (webkit || ie && ie_version == 9)
5977 setTimeout(function() {document.body.focus(); display.input.focus();}, 20);
5979 display.input.focus();
5982 // Let the drag handler handle this.
5983 if (webkit) display.scroller.draggable = true;
5984 cm.state.draggingText = dragEnd;
5985 // IE's approach to draggable
5986 if (display.scroller.dragDrop) display.scroller.dragDrop();
5987 on(document, "mouseup", dragEnd);
5988 on(display.scroller, "drop", dragEnd);
5991 // Normal selection, as opposed to text dragging.
5992 function leftButtonSelect(cm, e, start, type, addNew) {
5993 var display = cm.display, doc = cm.doc;
5994 e_preventDefault(e);
5996 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
5997 if (addNew && !e.shiftKey) {
5998 ourIndex = doc.sel.contains(start);
6000 ourRange = ranges[ourIndex];
6002 ourRange = new Range(start, start);
6004 ourRange = doc.sel.primary();
6005 ourIndex = doc.sel.primIndex;
6008 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) {
6010 if (!addNew) ourRange = new Range(start, start);
6011 start = posFromMouse(cm, e, true, true);
6013 } else if (type == "double") {
6014 var word = cm.findWordAt(start);
6015 if (cm.display.shift || doc.extend)
6016 ourRange = extendRange(doc, ourRange, word.anchor, word.head);
6019 } else if (type == "triple") {
6020 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
6021 if (cm.display.shift || doc.extend)
6022 ourRange = extendRange(doc, ourRange, line.anchor, line.head);
6026 ourRange = extendRange(doc, ourRange, start);
6031 setSelection(doc, new Selection([ourRange], 0), sel_mouse);
6033 } else if (ourIndex == -1) {
6034 ourIndex = ranges.length;
6035 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex),
6036 {scroll: false, origin: "*mouse"});
6037 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) {
6038 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
6039 {scroll: false, origin: "*mouse"});
6042 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
6045 var lastPos = start;
6046 function extendTo(pos) {
6047 if (cmp(lastPos, pos) == 0) return;
6050 if (type == "rect") {
6051 var ranges = [], tabSize = cm.options.tabSize;
6052 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
6053 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
6054 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
6055 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
6056 line <= end; line++) {
6057 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
6059 ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
6060 else if (text.length > leftPos)
6061 ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
6063 if (!ranges.length) ranges.push(new Range(start, start));
6064 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
6065 {origin: "*mouse", scroll: false});
6066 cm.scrollIntoView(pos);
6068 var oldRange = ourRange;
6069 var anchor = oldRange.anchor, head = pos;
6070 if (type != "single") {
6071 if (type == "double")
6072 var range = cm.findWordAt(pos);
6074 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
6075 if (cmp(range.anchor, anchor) > 0) {
6077 anchor = minPos(oldRange.from(), range.anchor);
6079 head = range.anchor;
6080 anchor = maxPos(oldRange.to(), range.head);
6083 var ranges = startSel.ranges.slice(0);
6084 ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
6085 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
6089 var editorSize = display.wrapper.getBoundingClientRect();
6090 // Used to ensure timeout re-tries don't fire when another extend
6091 // happened in the meantime (clearTimeout isn't reliable -- at
6092 // least on Chrome, the timeouts still happen even when cleared,
6093 // if the clear happens after their scheduled firing time).
6096 function extend(e) {
6097 var curCount = ++counter;
6098 var cur = posFromMouse(cm, e, true, type == "rect");
6100 if (cmp(cur, lastPos) != 0) {
6101 cm.curOp.focus = activeElt();
6103 var visible = visibleLines(display, doc);
6104 if (cur.line >= visible.to || cur.line < visible.from)
6105 setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
6107 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
6108 if (outside) setTimeout(operation(cm, function() {
6109 if (counter != curCount) return;
6110 display.scroller.scrollTop += outside;
6117 cm.state.selectingText = false;
6119 e_preventDefault(e);
6120 display.input.focus();
6121 off(document, "mousemove", move);
6122 off(document, "mouseup", up);
6123 doc.history.lastSelOrigin = null;
6126 var move = operation(cm, function(e) {
6127 if (!e_button(e)) done(e);
6130 var up = operation(cm, done);
6131 cm.state.selectingText = up;
6132 on(document, "mousemove", move);
6133 on(document, "mouseup", up);
6136 // Determines whether an event happened in the gutter, and fires the
6137 // handlers for the corresponding event.
6138 function gutterEvent(cm, e, type, prevent) {
6139 try { var mX = e.clientX, mY = e.clientY; }
6140 catch(e) { return false; }
6141 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
6142 if (prevent) e_preventDefault(e);
6144 var display = cm.display;
6145 var lineBox = display.lineDiv.getBoundingClientRect();
6147 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
6148 mY -= lineBox.top - display.viewOffset;
6150 for (var i = 0; i < cm.options.gutters.length; ++i) {
6151 var g = display.gutters.childNodes[i];
6152 if (g && g.getBoundingClientRect().right >= mX) {
6153 var line = lineAtHeight(cm.doc, mY);
6154 var gutter = cm.options.gutters[i];
6155 signal(cm, type, cm, line, gutter, e);
6156 return e_defaultPrevented(e);
6161 function clickInGutter(cm, e) {
6162 return gutterEvent(cm, e, "gutterClick", true);
6165 // Kludge to work around strange IE behavior where it'll sometimes
6166 // re-fire a series of drag-related events right after the drop (#1551)
6169 function onDrop(e) {
6171 clearDragCursor(cm);
6172 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
6174 e_preventDefault(e);
6175 if (ie) lastDrop = +new Date;
6176 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
6177 if (!pos || cm.isReadOnly()) return;
6178 // Might be a file drop, in which case we simply extract the text
6180 if (files && files.length && window.FileReader && window.File) {
6181 var n = files.length, text = Array(n), read = 0;
6182 var loadFile = function(file, i) {
6183 if (cm.options.allowDropFileTypes &&
6184 indexOf(cm.options.allowDropFileTypes, file.type) == -1)
6187 var reader = new FileReader;
6188 reader.onload = operation(cm, function() {
6189 var content = reader.result;
6190 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "";
6193 pos = clipPos(cm.doc, pos);
6194 var change = {from: pos, to: pos,
6195 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())),
6197 makeChange(cm.doc, change);
6198 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
6201 reader.readAsText(file);
6203 for (var i = 0; i < n; ++i) loadFile(files[i], i);
6204 } else { // Normal drop
6205 // Don't do a replace if the drop happened inside of the selected text.
6206 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
6207 cm.state.draggingText(e);
6208 // Ensure the editor is re-focused
6209 setTimeout(function() {cm.display.input.focus();}, 20);
6213 var text = e.dataTransfer.getData("Text");
6215 if (cm.state.draggingText && !(mac ? e.altKey : e.ctrlKey))
6216 var selected = cm.listSelections();
6217 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
6218 if (selected) for (var i = 0; i < selected.length; ++i)
6219 replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag");
6220 cm.replaceSelection(text, "around", "paste");
6221 cm.display.input.focus();
6228 function onDragStart(cm, e) {
6229 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
6230 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
6232 e.dataTransfer.setData("Text", cm.getSelection());
6233 e.dataTransfer.effectAllowed = "copyMove"
6235 // Use dummy image instead of default browsers image.
6236 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
6237 if (e.dataTransfer.setDragImage && !safari) {
6238 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
6239 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
6241 img.width = img.height = 1;
6242 cm.display.wrapper.appendChild(img);
6243 // Force a relayout, or Opera won't use our image for some obscure reason
6244 img._top = img.offsetTop;
6246 e.dataTransfer.setDragImage(img, 0, 0);
6247 if (presto) img.parentNode.removeChild(img);
6251 function onDragOver(cm, e) {
6252 var pos = posFromMouse(cm, e);
6254 var frag = document.createDocumentFragment();
6255 drawSelectionCursor(cm, pos, frag);
6256 if (!cm.display.dragCursor) {
6257 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
6258 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
6260 removeChildrenAndAdd(cm.display.dragCursor, frag);
6263 function clearDragCursor(cm) {
6264 if (cm.display.dragCursor) {
6265 cm.display.lineSpace.removeChild(cm.display.dragCursor);
6266 cm.display.dragCursor = null;
6272 // Sync the scrollable area and scrollbars, ensure the viewport
6273 // covers the visible area.
6274 function setScrollTop(cm, val) {
6275 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
6276 cm.doc.scrollTop = val;
6277 if (!gecko) updateDisplaySimple(cm, {top: val});
6278 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
6279 cm.display.scrollbars.setScrollTop(val);
6280 if (gecko) updateDisplaySimple(cm);
6281 startWorker(cm, 100);
6283 // Sync scroller and scrollbar, ensure the gutter elements are
6285 function setScrollLeft(cm, val, isScroller) {
6286 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
6287 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
6288 cm.doc.scrollLeft = val;
6289 alignHorizontally(cm);
6290 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
6291 cm.display.scrollbars.setScrollLeft(val);
6294 // Since the delta values reported on mouse wheel events are
6295 // unstandardized between browsers and even browser versions, and
6296 // generally horribly unpredictable, this code starts by measuring
6297 // the scroll effect that the first few mouse wheel events have,
6298 // and, from that, detects the way it can convert deltas to pixel
6299 // offsets afterwards.
6301 // The reason we want to know the amount a wheel event will scroll
6302 // is that it gives us a chance to update the display before the
6303 // actual scrolling happens, reducing flickering.
6305 var wheelSamples = 0, wheelPixelsPerUnit = null;
6306 // Fill in a browser-detected starting value on browsers where we
6307 // know one. These don't have to be accurate -- the result of them
6308 // being wrong would just be a slight flicker on the first wheel
6309 // scroll (if it is large enough).
6310 if (ie) wheelPixelsPerUnit = -.53;
6311 else if (gecko) wheelPixelsPerUnit = 15;
6312 else if (chrome) wheelPixelsPerUnit = -.7;
6313 else if (safari) wheelPixelsPerUnit = -1/3;
6315 var wheelEventDelta = function(e) {
6316 var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
6317 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
6318 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
6319 else if (dy == null) dy = e.wheelDelta;
6320 return {x: dx, y: dy};
6322 CodeMirror.wheelEventPixels = function(e) {
6323 var delta = wheelEventDelta(e);
6324 delta.x *= wheelPixelsPerUnit;
6325 delta.y *= wheelPixelsPerUnit;
6329 function onScrollWheel(cm, e) {
6330 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
6332 var display = cm.display, scroll = display.scroller;
6333 // Quit if there's nothing to scroll here
6334 var canScrollX = scroll.scrollWidth > scroll.clientWidth;
6335 var canScrollY = scroll.scrollHeight > scroll.clientHeight;
6336 if (!(dx && canScrollX || dy && canScrollY)) return;
6338 // Webkit browsers on OS X abort momentum scrolls when the target
6339 // of the scroll event is removed from the scrollable element.
6340 // This hack (see related code in patchDisplay) makes sure the
6341 // element is kept around.
6342 if (dy && mac && webkit) {
6343 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
6344 for (var i = 0; i < view.length; i++) {
6345 if (view[i].node == cur) {
6346 cm.display.currentWheelTarget = cur;
6353 // On some browsers, horizontal scrolling will cause redraws to
6354 // happen before the gutter has been realigned, causing it to
6355 // wriggle around in a most unseemly way. When we have an
6356 // estimated pixels/delta value, we just handle horizontal
6357 // scrolling entirely here. It'll be slightly off from native, but
6358 // better than glitching out.
6359 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
6360 if (dy && canScrollY)
6361 setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
6362 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
6363 // Only prevent default scrolling if vertical scrolling is
6364 // actually possible. Otherwise, it causes vertical scroll
6365 // jitter on OSX trackpads when deltaX is small and deltaY
6366 // is large (issue #3579)
6367 if (!dy || (dy && canScrollY))
6368 e_preventDefault(e);
6369 display.wheelStartX = null; // Abort measurement, if in progress
6373 // 'Project' the visible viewport to cover the area that is being
6374 // scrolled into view (if we know enough to estimate it).
6375 if (dy && wheelPixelsPerUnit != null) {
6376 var pixels = dy * wheelPixelsPerUnit;
6377 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
6378 if (pixels < 0) top = Math.max(0, top + pixels - 50);
6379 else bot = Math.min(cm.doc.height, bot + pixels + 50);
6380 updateDisplaySimple(cm, {top: top, bottom: bot});
6383 if (wheelSamples < 20) {
6384 if (display.wheelStartX == null) {
6385 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
6386 display.wheelDX = dx; display.wheelDY = dy;
6387 setTimeout(function() {
6388 if (display.wheelStartX == null) return;
6389 var movedX = scroll.scrollLeft - display.wheelStartX;
6390 var movedY = scroll.scrollTop - display.wheelStartY;
6391 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
6392 (movedX && display.wheelDX && movedX / display.wheelDX);
6393 display.wheelStartX = display.wheelStartY = null;
6394 if (!sample) return;
6395 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
6399 display.wheelDX += dx; display.wheelDY += dy;
6406 // Run a handler that was bound to a key.
6407 function doHandleBinding(cm, bound, dropShift) {
6408 if (typeof bound == "string") {
6409 bound = commands[bound];
6410 if (!bound) return false;
6412 // Ensure previous input has been read, so that the handler sees a
6413 // consistent view of the document
6414 cm.display.input.ensurePolled();
6415 var prevShift = cm.display.shift, done = false;
6417 if (cm.isReadOnly()) cm.state.suppressEdits = true;
6418 if (dropShift) cm.display.shift = false;
6419 done = bound(cm) != Pass;
6421 cm.display.shift = prevShift;
6422 cm.state.suppressEdits = false;
6427 function lookupKeyForEditor(cm, name, handle) {
6428 for (var i = 0; i < cm.state.keyMaps.length; i++) {
6429 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
6430 if (result) return result;
6432 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
6433 || lookupKey(name, cm.options.keyMap, handle, cm);
6436 var stopSeq = new Delayed;
6437 function dispatchKey(cm, name, e, handle) {
6438 var seq = cm.state.keySeq;
6440 if (isModifierKey(name)) return "handled";
6441 stopSeq.set(50, function() {
6442 if (cm.state.keySeq == seq) {
6443 cm.state.keySeq = null;
6444 cm.display.input.reset();
6447 name = seq + " " + name;
6449 var result = lookupKeyForEditor(cm, name, handle);
6451 if (result == "multi")
6452 cm.state.keySeq = name;
6453 if (result == "handled")
6454 signalLater(cm, "keyHandled", cm, name, e);
6456 if (result == "handled" || result == "multi") {
6457 e_preventDefault(e);
6461 if (seq && !result && /\'$/.test(name)) {
6462 e_preventDefault(e);
6468 // Handle a key from the keydown event.
6469 function handleKeyBinding(cm, e) {
6470 var name = keyName(e, true);
6471 if (!name) return false;
6473 if (e.shiftKey && !cm.state.keySeq) {
6474 // First try to resolve full name (including 'Shift-'). Failing
6475 // that, see if there is a cursor-motion command (starting with
6476 // 'go') bound to the keyname without 'Shift-'.
6477 return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
6478 || dispatchKey(cm, name, e, function(b) {
6479 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
6480 return doHandleBinding(cm, b);
6483 return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
6487 // Handle a key from the keypress event
6488 function handleCharBinding(cm, e, ch) {
6489 return dispatchKey(cm, "'" + ch + "'", e,
6490 function(b) { return doHandleBinding(cm, b, true); });
6493 var lastStoppedKey = null;
6494 function onKeyDown(e) {
6496 cm.curOp.focus = activeElt();
6497 if (signalDOMEvent(cm, e)) return;
6498 // IE does strange things with escape.
6499 if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false;
6500 var code = e.keyCode;
6501 cm.display.shift = code == 16 || e.shiftKey;
6502 var handled = handleKeyBinding(cm, e);
6504 lastStoppedKey = handled ? code : null;
6505 // Opera has no cut event... we try to at least catch the key combo
6506 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
6507 cm.replaceSelection("", null, "cut");
6510 // Turn mouse into crosshair when Alt is held on Mac.
6511 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
6515 function showCrossHair(cm) {
6516 var lineDiv = cm.display.lineDiv;
6517 addClass(lineDiv, "CodeMirror-crosshair");
6520 if (e.keyCode == 18 || !e.altKey) {
6521 rmClass(lineDiv, "CodeMirror-crosshair");
6522 off(document, "keyup", up);
6523 off(document, "mouseover", up);
6526 on(document, "keyup", up);
6527 on(document, "mouseover", up);
6530 function onKeyUp(e) {
6531 if (e.keyCode == 16) this.doc.sel.shift = false;
6532 signalDOMEvent(this, e);
6535 function onKeyPress(e) {
6537 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
6538 var keyCode = e.keyCode, charCode = e.charCode;
6539 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
6540 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return;
6541 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
6542 if (handleCharBinding(cm, e, ch)) return;
6543 cm.display.input.onKeyPress(e);
6546 // FOCUS/BLUR EVENTS
6548 function delayBlurEvent(cm) {
6549 cm.state.delayingBlurEvent = true;
6550 setTimeout(function() {
6551 if (cm.state.delayingBlurEvent) {
6552 cm.state.delayingBlurEvent = false;
6558 function onFocus(cm) {
6559 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false;
6561 if (cm.options.readOnly == "nocursor") return;
6562 if (!cm.state.focused) {
6563 signal(cm, "focus", cm);
6564 cm.state.focused = true;
6565 addClass(cm.display.wrapper, "CodeMirror-focused");
6566 // This test prevents this from firing when a context
6567 // menu is closed (since the input reset would kill the
6568 // select-all detection hack)
6569 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
6570 cm.display.input.reset();
6571 if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730
6573 cm.display.input.receivedFocus();
6577 function onBlur(cm) {
6578 if (cm.state.delayingBlurEvent) return;
6580 if (cm.state.focused) {
6581 signal(cm, "blur", cm);
6582 cm.state.focused = false;
6583 rmClass(cm.display.wrapper, "CodeMirror-focused");
6585 clearInterval(cm.display.blinker);
6586 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
6589 // CONTEXT MENU HANDLING
6591 // To make the context menu work, we need to briefly unhide the
6592 // textarea (making it as unobtrusive as possible) to let the
6593 // right-click take effect on it.
6594 function onContextMenu(cm, e) {
6595 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return;
6596 if (signalDOMEvent(cm, e, "contextmenu")) return;
6597 cm.display.input.onContextMenu(e);
6600 function contextMenuInGutter(cm, e) {
6601 if (!hasHandler(cm, "gutterContextMenu")) return false;
6602 return gutterEvent(cm, e, "gutterContextMenu", false);
6607 // Compute the position of the end of a change (its 'to' property
6608 // refers to the pre-change end).
6609 var changeEnd = CodeMirror.changeEnd = function(change) {
6610 if (!change.text) return change.to;
6611 return Pos(change.from.line + change.text.length - 1,
6612 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
6615 // Adjust a position to refer to the post-change position of the
6616 // same text, or the end of the change if the change covers it.
6617 function adjustForChange(pos, change) {
6618 if (cmp(pos, change.from) < 0) return pos;
6619 if (cmp(pos, change.to) <= 0) return changeEnd(change);
6621 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
6622 if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
6623 return Pos(line, ch);
6626 function computeSelAfterChange(doc, change) {
6628 for (var i = 0; i < doc.sel.ranges.length; i++) {
6629 var range = doc.sel.ranges[i];
6630 out.push(new Range(adjustForChange(range.anchor, change),
6631 adjustForChange(range.head, change)));
6633 return normalizeSelection(out, doc.sel.primIndex);
6636 function offsetPos(pos, old, nw) {
6637 if (pos.line == old.line)
6638 return Pos(nw.line, pos.ch - old.ch + nw.ch);
6640 return Pos(nw.line + (pos.line - old.line), pos.ch);
6643 // Used by replaceSelections to allow moving the selection to the
6644 // start or around the replaced test. Hint may be "start" or "around".
6645 function computeReplacedSel(doc, changes, hint) {
6647 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
6648 for (var i = 0; i < changes.length; i++) {
6649 var change = changes[i];
6650 var from = offsetPos(change.from, oldPrev, newPrev);
6651 var to = offsetPos(changeEnd(change), oldPrev, newPrev);
6652 oldPrev = change.to;
6654 if (hint == "around") {
6655 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
6656 out[i] = new Range(inv ? to : from, inv ? from : to);
6658 out[i] = new Range(from, from);
6661 return new Selection(out, doc.sel.primIndex);
6664 // Allow "beforeChange" event handlers to influence a change
6665 function filterChange(doc, change, update) {
6671 origin: change.origin,
6672 cancel: function() { this.canceled = true; }
6674 if (update) obj.update = function(from, to, text, origin) {
6675 if (from) this.from = clipPos(doc, from);
6676 if (to) this.to = clipPos(doc, to);
6677 if (text) this.text = text;
6678 if (origin !== undefined) this.origin = origin;
6680 signal(doc, "beforeChange", doc, obj);
6681 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
6683 if (obj.canceled) return null;
6684 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
6687 // Apply a change to a document, and add it to the document's
6688 // history, and propagating it to all linked documents.
6689 function makeChange(doc, change, ignoreReadOnly) {
6691 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
6692 if (doc.cm.state.suppressEdits) return;
6695 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
6696 change = filterChange(doc, change, true);
6697 if (!change) return;
6700 // Possibly split or suppress the update based on the presence
6701 // of read-only spans in its range.
6702 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
6704 for (var i = split.length - 1; i >= 0; --i)
6705 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text});
6707 makeChangeInner(doc, change);
6711 function makeChangeInner(doc, change) {
6712 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return;
6713 var selAfter = computeSelAfterChange(doc, change);
6714 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
6716 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
6719 linkedDocs(doc, function(doc, sharedHist) {
6720 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6721 rebaseHist(doc.history, change);
6722 rebased.push(doc.history);
6724 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
6728 // Revert a change stored in a document's history.
6729 function makeChangeFromHistory(doc, type, allowSelectionOnly) {
6730 if (doc.cm && doc.cm.state.suppressEdits) return;
6732 var hist = doc.history, event, selAfter = doc.sel;
6733 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
6735 // Verify that there is a useable event (so that ctrl-z won't
6736 // needlessly clear selection events)
6737 for (var i = 0; i < source.length; i++) {
6739 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
6742 if (i == source.length) return;
6743 hist.lastOrigin = hist.lastSelOrigin = null;
6746 event = source.pop();
6748 pushSelectionToHistory(event, dest);
6749 if (allowSelectionOnly && !event.equals(doc.sel)) {
6750 setSelection(doc, event, {clearRedo: false});
6758 // Build up a reverse change object to add to the opposite history
6759 // stack (redo when undoing, and vice versa).
6760 var antiChanges = [];
6761 pushSelectionToHistory(selAfter, dest);
6762 dest.push({changes: antiChanges, generation: hist.generation});
6763 hist.generation = event.generation || ++hist.maxGeneration;
6765 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
6767 for (var i = event.changes.length - 1; i >= 0; --i) {
6768 var change = event.changes[i];
6769 change.origin = type;
6770 if (filter && !filterChange(doc, change, false)) {
6775 antiChanges.push(historyChangeFromChange(doc, change));
6777 var after = i ? computeSelAfterChange(doc, change) : lst(source);
6778 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
6779 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)});
6782 // Propagate to the linked documents
6783 linkedDocs(doc, function(doc, sharedHist) {
6784 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
6785 rebaseHist(doc.history, change);
6786 rebased.push(doc.history);
6788 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
6793 // Sub-views need their line numbers shifted when text is added
6794 // above or below them in the parent document.
6795 function shiftDoc(doc, distance) {
6796 if (distance == 0) return;
6797 doc.first += distance;
6798 doc.sel = new Selection(map(doc.sel.ranges, function(range) {
6799 return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
6800 Pos(range.head.line + distance, range.head.ch));
6801 }), doc.sel.primIndex);
6803 regChange(doc.cm, doc.first, doc.first - distance, distance);
6804 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
6805 regLineChange(doc.cm, l, "gutter");
6809 // More lower-level change function, handling only a single document
6810 // (not linked ones).
6811 function makeChangeSingleDoc(doc, change, selAfter, spans) {
6812 if (doc.cm && !doc.cm.curOp)
6813 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
6815 if (change.to.line < doc.first) {
6816 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
6819 if (change.from.line > doc.lastLine()) return;
6821 // Clip the change to the size of this doc
6822 if (change.from.line < doc.first) {
6823 var shift = change.text.length - 1 - (doc.first - change.from.line);
6824 shiftDoc(doc, shift);
6825 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
6826 text: [lst(change.text)], origin: change.origin};
6828 var last = doc.lastLine();
6829 if (change.to.line > last) {
6830 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
6831 text: [change.text[0]], origin: change.origin};
6834 change.removed = getBetween(doc, change.from, change.to);
6836 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
6837 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
6838 else updateDoc(doc, change, spans);
6839 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
6842 // Handle the interaction of a change to a document with the editor
6843 // that this document is part of.
6844 function makeChangeSingleDocInEditor(cm, change, spans) {
6845 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
6847 var recomputeMaxLength = false, checkWidthStart = from.line;
6848 if (!cm.options.lineWrapping) {
6849 checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
6850 doc.iter(checkWidthStart, to.line + 1, function(line) {
6851 if (line == display.maxLine) {
6852 recomputeMaxLength = true;
6858 if (doc.sel.contains(change.from, change.to) > -1)
6859 signalCursorActivity(cm);
6861 updateDoc(doc, change, spans, estimateHeight(cm));
6863 if (!cm.options.lineWrapping) {
6864 doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
6865 var len = lineLength(line);
6866 if (len > display.maxLineLength) {
6867 display.maxLine = line;
6868 display.maxLineLength = len;
6869 display.maxLineChanged = true;
6870 recomputeMaxLength = false;
6873 if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
6876 // Adjust frontier, schedule worker
6877 doc.frontier = Math.min(doc.frontier, from.line);
6878 startWorker(cm, 400);
6880 var lendiff = change.text.length - (to.line - from.line) - 1;
6881 // Remember that these lines changed, for updating the display
6884 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
6885 regLineChange(cm, from.line, "text");
6887 regChange(cm, from.line, to.line + 1, lendiff);
6889 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
6890 if (changeHandler || changesHandler) {
6894 removed: change.removed,
6895 origin: change.origin
6897 if (changeHandler) signalLater(cm, "change", cm, obj);
6898 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj);
6900 cm.display.selForContextMenu = null;
6903 function replaceRange(doc, code, from, to, origin) {
6905 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; }
6906 if (typeof code == "string") code = doc.splitLines(code);
6907 makeChange(doc, {from: from, to: to, text: code, origin: origin});
6910 // SCROLLING THINGS INTO VIEW
6912 // If an editor sits on the top or bottom of the window, partially
6913 // scrolled out of view, this ensures that the cursor is visible.
6914 function maybeScrollWindow(cm, coords) {
6915 if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
6917 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
6918 if (coords.top + box.top < 0) doScroll = true;
6919 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
6920 if (doScroll != null && !phantom) {
6921 var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " +
6922 (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " +
6923 (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " +
6924 coords.left + "px; width: 2px;");
6925 cm.display.lineSpace.appendChild(scrollNode);
6926 scrollNode.scrollIntoView(doScroll);
6927 cm.display.lineSpace.removeChild(scrollNode);
6931 // Scroll a given position into view (immediately), verifying that
6932 // it actually became visible (as line heights are accurately
6933 // measured, the position of something may 'drift' during drawing).
6934 function scrollPosIntoView(cm, pos, end, margin) {
6935 if (margin == null) margin = 0;
6936 for (var limit = 0; limit < 5; limit++) {
6937 var changed = false, coords = cursorCoords(cm, pos);
6938 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
6939 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
6940 Math.min(coords.top, endCoords.top) - margin,
6941 Math.max(coords.left, endCoords.left),
6942 Math.max(coords.bottom, endCoords.bottom) + margin);
6943 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
6944 if (scrollPos.scrollTop != null) {
6945 setScrollTop(cm, scrollPos.scrollTop);
6946 if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
6948 if (scrollPos.scrollLeft != null) {
6949 setScrollLeft(cm, scrollPos.scrollLeft);
6950 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
6952 if (!changed) break;
6957 // Scroll a given set of coordinates into view (immediately).
6958 function scrollIntoView(cm, x1, y1, x2, y2) {
6959 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
6960 if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
6961 if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
6964 // Calculate a new scroll position needed to scroll the given
6965 // rectangle into view. Returns an object with scrollTop and
6966 // scrollLeft properties. When these are undefined, the
6967 // vertical/horizontal position does not need to be adjusted.
6968 function calculateScrollPos(cm, x1, y1, x2, y2) {
6969 var display = cm.display, snapMargin = textHeight(cm.display);
6971 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
6972 var screen = displayHeight(cm), result = {};
6973 if (y2 - y1 > screen) y2 = y1 + screen;
6974 var docBottom = cm.doc.height + paddingVert(display);
6975 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
6976 if (y1 < screentop) {
6977 result.scrollTop = atTop ? 0 : y1;
6978 } else if (y2 > screentop + screen) {
6979 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
6980 if (newTop != screentop) result.scrollTop = newTop;
6983 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
6984 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
6985 var tooWide = x2 - x1 > screenw;
6986 if (tooWide) x2 = x1 + screenw;
6988 result.scrollLeft = 0;
6989 else if (x1 < screenleft)
6990 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
6991 else if (x2 > screenw + screenleft - 3)
6992 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
6996 // Store a relative adjustment to the scroll position in the current
6997 // operation (to be applied when the operation finishes).
6998 function addToScrollPos(cm, left, top) {
6999 if (left != null || top != null) resolveScrollToPos(cm);
7001 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
7003 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
7006 // Make sure that at the end of the operation the current cursor is
7008 function ensureCursorVisible(cm) {
7009 resolveScrollToPos(cm);
7010 var cur = cm.getCursor(), from = cur, to = cur;
7011 if (!cm.options.lineWrapping) {
7012 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
7013 to = Pos(cur.line, cur.ch + 1);
7015 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
7018 // When an operation has its scrollToPos property set, and another
7019 // scroll action is applied before the end of the operation, this
7020 // 'simulates' scrolling that position into view in a cheap way, so
7021 // that the effect of intermediate scroll commands is not ignored.
7022 function resolveScrollToPos(cm) {
7023 var range = cm.curOp.scrollToPos;
7025 cm.curOp.scrollToPos = null;
7026 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
7027 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
7028 Math.min(from.top, to.top) - range.margin,
7029 Math.max(from.right, to.right),
7030 Math.max(from.bottom, to.bottom) + range.margin);
7031 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
7037 // Indent the given line. The how parameter can be "smart",
7038 // "add"/null, "subtract", or "prev". When aggressive is false
7039 // (typically set to true for forced single-line indents), empty
7040 // lines are not indented, and places where the mode returns Pass
7042 function indentLine(cm, n, how, aggressive) {
7043 var doc = cm.doc, state;
7044 if (how == null) how = "add";
7045 if (how == "smart") {
7046 // Fall back to "prev" when the mode doesn't have an indentation
7048 if (!doc.mode.indent) how = "prev";
7049 else state = getStateBefore(cm, n);
7052 var tabSize = cm.options.tabSize;
7053 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
7054 if (line.stateAfter) line.stateAfter = null;
7055 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
7056 if (!aggressive && !/\S/.test(line.text)) {
7059 } else if (how == "smart") {
7060 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
7061 if (indentation == Pass || indentation > 150) {
7062 if (!aggressive) return;
7066 if (how == "prev") {
7067 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
7068 else indentation = 0;
7069 } else if (how == "add") {
7070 indentation = curSpace + cm.options.indentUnit;
7071 } else if (how == "subtract") {
7072 indentation = curSpace - cm.options.indentUnit;
7073 } else if (typeof how == "number") {
7074 indentation = curSpace + how;
7076 indentation = Math.max(0, indentation);
7078 var indentString = "", pos = 0;
7079 if (cm.options.indentWithTabs)
7080 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
7081 if (pos < indentation) indentString += spaceStr(indentation - pos);
7083 if (indentString != curSpaceString) {
7084 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
7085 line.stateAfter = null;
7088 // Ensure that, if the cursor was in the whitespace at the start
7089 // of the line, it is moved to the end of that space.
7090 for (var i = 0; i < doc.sel.ranges.length; i++) {
7091 var range = doc.sel.ranges[i];
7092 if (range.head.line == n && range.head.ch < curSpaceString.length) {
7093 var pos = Pos(n, curSpaceString.length);
7094 replaceOneSelection(doc, i, new Range(pos, pos));
7101 // Utility for applying a change to a line by handle or number,
7102 // returning the number and optionally registering the line as
7104 function changeLine(doc, handle, changeType, op) {
7105 var no = handle, line = handle;
7106 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
7107 else no = lineNo(handle);
7108 if (no == null) return null;
7109 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType);
7113 // Helper for deleting text near the selection(s), used to implement
7114 // backspace, delete, and similar functionality.
7115 function deleteNearSelection(cm, compute) {
7116 var ranges = cm.doc.sel.ranges, kill = [];
7117 // Build up a set of ranges to kill first, merging overlapping
7119 for (var i = 0; i < ranges.length; i++) {
7120 var toKill = compute(ranges[i]);
7121 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
7122 var replaced = kill.pop();
7123 if (cmp(replaced.from, toKill.from) < 0) {
7124 toKill.from = replaced.from;
7130 // Next, remove those actual ranges.
7131 runInOp(cm, function() {
7132 for (var i = kill.length - 1; i >= 0; i--)
7133 replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete");
7134 ensureCursorVisible(cm);
7138 // Used for horizontal relative motion. Dir is -1 or 1 (left or
7139 // right), unit can be "char", "column" (like char, but doesn't
7140 // cross line boundaries), "word" (across next word), or "group" (to
7141 // the start of next group of word or non-word-non-whitespace
7142 // chars). The visually param controls whether, in right-to-left
7143 // text, direction 1 means to move towards the next index in the
7144 // string, or towards the character to the right of the current
7145 // position. The resulting position will have a hitSide=true
7146 // property if it reached the end of the document.
7147 function findPosH(doc, pos, dir, unit, visually) {
7148 var line = pos.line, ch = pos.ch, origDir = dir;
7149 var lineObj = getLine(doc, line);
7150 function findNextLine() {
7152 if (l < doc.first || l >= doc.first + doc.size) return false
7154 return lineObj = getLine(doc, l);
7156 function moveOnce(boundToLine) {
7157 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
7159 if (!boundToLine && findNextLine()) {
7160 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
7161 else ch = dir < 0 ? lineObj.text.length : 0;
7167 if (unit == "char") {
7169 } else if (unit == "column") {
7171 } else if (unit == "word" || unit == "group") {
7172 var sawType = null, group = unit == "group";
7173 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
7174 for (var first = true;; first = false) {
7175 if (dir < 0 && !moveOnce(!first)) break;
7176 var cur = lineObj.text.charAt(ch) || "\n";
7177 var type = isWordChar(cur, helper) ? "w"
7178 : group && cur == "\n" ? "n"
7179 : !group || /\s/.test(cur) ? null
7181 if (group && !first && !type) type = "s";
7182 if (sawType && sawType != type) {
7183 if (dir < 0) {dir = 1; moveOnce();}
7187 if (type) sawType = type;
7188 if (dir > 0 && !moveOnce(!first)) break;
7191 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true);
7192 if (!cmp(pos, result)) result.hitSide = true;
7196 // For relative vertical movement. Dir may be -1 or 1. Unit can be
7197 // "page" or "line". The resulting position will have a hitSide=true
7198 // property if it reached the end of the document.
7199 function findPosV(cm, pos, dir, unit) {
7200 var doc = cm.doc, x = pos.left, y;
7201 if (unit == "page") {
7202 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
7203 y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
7204 } else if (unit == "line") {
7205 y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
7208 var target = coordsChar(cm, x, y);
7209 if (!target.outside) break;
7210 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
7218 // The publicly visible API. Note that methodOp(f) means
7219 // 'wrap f in an operation, performed on its `this` parameter'.
7221 // This is not the complete set of editor methods. Most of the
7222 // methods defined on the Doc type are also injected into
7223 // CodeMirror.prototype, for backwards compatibility and
7226 CodeMirror.prototype = {
7227 constructor: CodeMirror,
7228 focus: function(){window.focus(); this.display.input.focus();},
7230 setOption: function(option, value) {
7231 var options = this.options, old = options[option];
7232 if (options[option] == value && option != "mode") return;
7233 options[option] = value;
7234 if (optionHandlers.hasOwnProperty(option))
7235 operation(this, optionHandlers[option])(this, value, old);
7238 getOption: function(option) {return this.options[option];},
7239 getDoc: function() {return this.doc;},
7241 addKeyMap: function(map, bottom) {
7242 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
7244 removeKeyMap: function(map) {
7245 var maps = this.state.keyMaps;
7246 for (var i = 0; i < maps.length; ++i)
7247 if (maps[i] == map || maps[i].name == map) {
7253 addOverlay: methodOp(function(spec, options) {
7254 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
7255 if (mode.startState) throw new Error("Overlays may not be stateful.");
7256 this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
7257 this.state.modeGen++;
7260 removeOverlay: methodOp(function(spec) {
7261 var overlays = this.state.overlays;
7262 for (var i = 0; i < overlays.length; ++i) {
7263 var cur = overlays[i].modeSpec;
7264 if (cur == spec || typeof spec == "string" && cur.name == spec) {
7265 overlays.splice(i, 1);
7266 this.state.modeGen++;
7273 indentLine: methodOp(function(n, dir, aggressive) {
7274 if (typeof dir != "string" && typeof dir != "number") {
7275 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
7276 else dir = dir ? "add" : "subtract";
7278 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
7280 indentSelection: methodOp(function(how) {
7281 var ranges = this.doc.sel.ranges, end = -1;
7282 for (var i = 0; i < ranges.length; i++) {
7283 var range = ranges[i];
7284 if (!range.empty()) {
7285 var from = range.from(), to = range.to();
7286 var start = Math.max(end, from.line);
7287 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
7288 for (var j = start; j < end; ++j)
7289 indentLine(this, j, how);
7290 var newRanges = this.doc.sel.ranges;
7291 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
7292 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll);
7293 } else if (range.head.line > end) {
7294 indentLine(this, range.head.line, how, true);
7295 end = range.head.line;
7296 if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
7301 // Fetch the parser token for a given character. Useful for hacks
7302 // that want to inspect the mode state (say, for completion).
7303 getTokenAt: function(pos, precise) {
7304 return takeToken(this, pos, precise);
7307 getLineTokens: function(line, precise) {
7308 return takeToken(this, Pos(line), precise, true);
7311 getTokenTypeAt: function(pos) {
7312 pos = clipPos(this.doc, pos);
7313 var styles = getLineStyles(this, getLine(this.doc, pos.line));
7314 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
7316 if (ch == 0) type = styles[2];
7318 var mid = (before + after) >> 1;
7319 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
7320 else if (styles[mid * 2 + 1] < ch) before = mid + 1;
7321 else { type = styles[mid * 2 + 2]; break; }
7323 var cut = type ? type.indexOf("cm-overlay ") : -1;
7324 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1);
7327 getModeAt: function(pos) {
7328 var mode = this.doc.mode;
7329 if (!mode.innerMode) return mode;
7330 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode;
7333 getHelper: function(pos, type) {
7334 return this.getHelpers(pos, type)[0];
7337 getHelpers: function(pos, type) {
7339 if (!helpers.hasOwnProperty(type)) return found;
7340 var help = helpers[type], mode = this.getModeAt(pos);
7341 if (typeof mode[type] == "string") {
7342 if (help[mode[type]]) found.push(help[mode[type]]);
7343 } else if (mode[type]) {
7344 for (var i = 0; i < mode[type].length; i++) {
7345 var val = help[mode[type][i]];
7346 if (val) found.push(val);
7348 } else if (mode.helperType && help[mode.helperType]) {
7349 found.push(help[mode.helperType]);
7350 } else if (help[mode.name]) {
7351 found.push(help[mode.name]);
7353 for (var i = 0; i < help._global.length; i++) {
7354 var cur = help._global[i];
7355 if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
7356 found.push(cur.val);
7361 getStateAfter: function(line, precise) {
7363 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
7364 return getStateBefore(this, line + 1, precise);
7367 cursorCoords: function(start, mode) {
7368 var pos, range = this.doc.sel.primary();
7369 if (start == null) pos = range.head;
7370 else if (typeof start == "object") pos = clipPos(this.doc, start);
7371 else pos = start ? range.from() : range.to();
7372 return cursorCoords(this, pos, mode || "page");
7375 charCoords: function(pos, mode) {
7376 return charCoords(this, clipPos(this.doc, pos), mode || "page");
7379 coordsChar: function(coords, mode) {
7380 coords = fromCoordSystem(this, coords, mode || "page");
7381 return coordsChar(this, coords.left, coords.top);
7384 lineAtHeight: function(height, mode) {
7385 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
7386 return lineAtHeight(this.doc, height + this.display.viewOffset);
7388 heightAtLine: function(line, mode) {
7389 var end = false, lineObj;
7390 if (typeof line == "number") {
7391 var last = this.doc.first + this.doc.size - 1;
7392 if (line < this.doc.first) line = this.doc.first;
7393 else if (line > last) { line = last; end = true; }
7394 lineObj = getLine(this.doc, line);
7398 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top +
7399 (end ? this.doc.height - heightAtLine(lineObj) : 0);
7402 defaultTextHeight: function() { return textHeight(this.display); },
7403 defaultCharWidth: function() { return charWidth(this.display); },
7405 setGutterMarker: methodOp(function(line, gutterID, value) {
7406 return changeLine(this.doc, line, "gutter", function(line) {
7407 var markers = line.gutterMarkers || (line.gutterMarkers = {});
7408 markers[gutterID] = value;
7409 if (!value && isEmpty(markers)) line.gutterMarkers = null;
7414 clearGutter: methodOp(function(gutterID) {
7415 var cm = this, doc = cm.doc, i = doc.first;
7416 doc.iter(function(line) {
7417 if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
7418 line.gutterMarkers[gutterID] = null;
7419 regLineChange(cm, i, "gutter");
7420 if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
7426 lineInfo: function(line) {
7427 if (typeof line == "number") {
7428 if (!isLine(this.doc, line)) return null;
7430 line = getLine(this.doc, line);
7431 if (!line) return null;
7433 var n = lineNo(line);
7434 if (n == null) return null;
7436 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
7437 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
7438 widgets: line.widgets};
7441 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
7443 addWidget: function(pos, node, scroll, vert, horiz) {
7444 var display = this.display;
7445 pos = cursorCoords(this, clipPos(this.doc, pos));
7446 var top = pos.bottom, left = pos.left;
7447 node.style.position = "absolute";
7448 node.setAttribute("cm-ignore-events", "true");
7449 this.display.input.setUneditable(node);
7450 display.sizer.appendChild(node);
7451 if (vert == "over") {
7453 } else if (vert == "above" || vert == "near") {
7454 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
7455 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
7456 // Default to positioning above (if specified and possible); otherwise default to positioning below
7457 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
7458 top = pos.top - node.offsetHeight;
7459 else if (pos.bottom + node.offsetHeight <= vspace)
7461 if (left + node.offsetWidth > hspace)
7462 left = hspace - node.offsetWidth;
7464 node.style.top = top + "px";
7465 node.style.left = node.style.right = "";
7466 if (horiz == "right") {
7467 left = display.sizer.clientWidth - node.offsetWidth;
7468 node.style.right = "0px";
7470 if (horiz == "left") left = 0;
7471 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
7472 node.style.left = left + "px";
7475 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
7478 triggerOnKeyDown: methodOp(onKeyDown),
7479 triggerOnKeyPress: methodOp(onKeyPress),
7480 triggerOnKeyUp: onKeyUp,
7482 execCommand: function(cmd) {
7483 if (commands.hasOwnProperty(cmd))
7484 return commands[cmd].call(null, this);
7487 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
7489 findPosH: function(from, amount, unit, visually) {
7491 if (amount < 0) { dir = -1; amount = -amount; }
7492 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
7493 cur = findPosH(this.doc, cur, dir, unit, visually);
7494 if (cur.hitSide) break;
7499 moveH: methodOp(function(dir, unit) {
7501 cm.extendSelectionsBy(function(range) {
7502 if (cm.display.shift || cm.doc.extend || range.empty())
7503 return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
7505 return dir < 0 ? range.from() : range.to();
7509 deleteH: methodOp(function(dir, unit) {
7510 var sel = this.doc.sel, doc = this.doc;
7511 if (sel.somethingSelected())
7512 doc.replaceSelection("", null, "+delete");
7514 deleteNearSelection(this, function(range) {
7515 var other = findPosH(doc, range.head, dir, unit, false);
7516 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other};
7520 findPosV: function(from, amount, unit, goalColumn) {
7521 var dir = 1, x = goalColumn;
7522 if (amount < 0) { dir = -1; amount = -amount; }
7523 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
7524 var coords = cursorCoords(this, cur, "div");
7525 if (x == null) x = coords.left;
7526 else coords.left = x;
7527 cur = findPosV(this, coords, dir, unit);
7528 if (cur.hitSide) break;
7533 moveV: methodOp(function(dir, unit) {
7534 var cm = this, doc = this.doc, goals = [];
7535 var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected();
7536 doc.extendSelectionsBy(function(range) {
7538 return dir < 0 ? range.from() : range.to();
7539 var headPos = cursorCoords(cm, range.head, "div");
7540 if (range.goalColumn != null) headPos.left = range.goalColumn;
7541 goals.push(headPos.left);
7542 var pos = findPosV(cm, headPos, dir, unit);
7543 if (unit == "page" && range == doc.sel.primary())
7544 addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top);
7547 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++)
7548 doc.sel.ranges[i].goalColumn = goals[i];
7551 // Find the word at the given position (as returned by coordsChar).
7552 findWordAt: function(pos) {
7553 var doc = this.doc, line = getLine(doc, pos.line).text;
7554 var start = pos.ch, end = pos.ch;
7556 var helper = this.getHelper(pos, "wordChars");
7557 if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
7558 var startChar = line.charAt(start);
7559 var check = isWordChar(startChar, helper)
7560 ? function(ch) { return isWordChar(ch, helper); }
7561 : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
7562 : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
7563 while (start > 0 && check(line.charAt(start - 1))) --start;
7564 while (end < line.length && check(line.charAt(end))) ++end;
7566 return new Range(Pos(pos.line, start), Pos(pos.line, end));
7569 toggleOverwrite: function(value) {
7570 if (value != null && value == this.state.overwrite) return;
7571 if (this.state.overwrite = !this.state.overwrite)
7572 addClass(this.display.cursorDiv, "CodeMirror-overwrite");
7574 rmClass(this.display.cursorDiv, "CodeMirror-overwrite");
7576 signal(this, "overwriteToggle", this, this.state.overwrite);
7578 hasFocus: function() { return this.display.input.getField() == activeElt(); },
7579 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit); },
7581 scrollTo: methodOp(function(x, y) {
7582 if (x != null || y != null) resolveScrollToPos(this);
7583 if (x != null) this.curOp.scrollLeft = x;
7584 if (y != null) this.curOp.scrollTop = y;
7586 getScrollInfo: function() {
7587 var scroller = this.display.scroller;
7588 return {left: scroller.scrollLeft, top: scroller.scrollTop,
7589 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
7590 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
7591 clientHeight: displayHeight(this), clientWidth: displayWidth(this)};
7594 scrollIntoView: methodOp(function(range, margin) {
7595 if (range == null) {
7596 range = {from: this.doc.sel.primary().head, to: null};
7597 if (margin == null) margin = this.options.cursorScrollMargin;
7598 } else if (typeof range == "number") {
7599 range = {from: Pos(range, 0), to: null};
7600 } else if (range.from == null) {
7601 range = {from: range, to: null};
7603 if (!range.to) range.to = range.from;
7604 range.margin = margin || 0;
7606 if (range.from.line != null) {
7607 resolveScrollToPos(this);
7608 this.curOp.scrollToPos = range;
7610 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
7611 Math.min(range.from.top, range.to.top) - range.margin,
7612 Math.max(range.from.right, range.to.right),
7613 Math.max(range.from.bottom, range.to.bottom) + range.margin);
7614 this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
7618 setSize: methodOp(function(width, height) {
7620 function interpret(val) {
7621 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
7623 if (width != null) cm.display.wrapper.style.width = interpret(width);
7624 if (height != null) cm.display.wrapper.style.height = interpret(height);
7625 if (cm.options.lineWrapping) clearLineMeasurementCache(this);
7626 var lineNo = cm.display.viewFrom;
7627 cm.doc.iter(lineNo, cm.display.viewTo, function(line) {
7628 if (line.widgets) for (var i = 0; i < line.widgets.length; i++)
7629 if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; }
7632 cm.curOp.forceUpdate = true;
7633 signal(cm, "refresh", this);
7636 operation: function(f){return runInOp(this, f);},
7638 refresh: methodOp(function() {
7639 var oldHeight = this.display.cachedTextHeight;
7641 this.curOp.forceUpdate = true;
7643 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
7644 updateGutterSpace(this);
7645 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5)
7646 estimateLineHeights(this);
7647 signal(this, "refresh", this);
7650 swapDoc: methodOp(function(doc) {
7653 attachDoc(this, doc);
7655 this.display.input.reset();
7656 this.scrollTo(doc.scrollLeft, doc.scrollTop);
7657 this.curOp.forceScroll = true;
7658 signalLater(this, "swapDoc", this, old);
7662 getInputField: function(){return this.display.input.getField();},
7663 getWrapperElement: function(){return this.display.wrapper;},
7664 getScrollerElement: function(){return this.display.scroller;},
7665 getGutterElement: function(){return this.display.gutters;}
7667 eventMixin(CodeMirror);
7671 // The default configuration options.
7672 var defaults = CodeMirror.defaults = {};
7673 // Functions to run when options are changed.
7674 var optionHandlers = CodeMirror.optionHandlers = {};
7676 function option(name, deflt, handle, notOnInit) {
7677 CodeMirror.defaults[name] = deflt;
7678 if (handle) optionHandlers[name] =
7679 notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
7682 // Passed to option handlers when there is no old value.
7683 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
7685 // These two are, on init, called from the constructor because they
7686 // have to be initialized before the editor can start at all.
7687 option("value", "", function(cm, val) {
7690 option("mode", null, function(cm, val) {
7691 cm.doc.modeOption = val;
7695 option("indentUnit", 2, loadMode, true);
7696 option("indentWithTabs", false);
7697 option("smartIndent", true);
7698 option("tabSize", 4, function(cm) {
7703 option("lineSeparator", null, function(cm, val) {
7704 cm.doc.lineSep = val;
7706 var newBreaks = [], lineNo = cm.doc.first;
7707 cm.doc.iter(function(line) {
7708 for (var pos = 0;;) {
7709 var found = line.text.indexOf(val, pos);
7710 if (found == -1) break;
7711 pos = found + val.length;
7712 newBreaks.push(Pos(lineNo, found));
7716 for (var i = newBreaks.length - 1; i >= 0; i--)
7717 replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length))
7719 option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) {
7720 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
7721 if (old != CodeMirror.Init) cm.refresh();
7723 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true);
7724 option("electricChars", true);
7725 option("inputStyle", mobile ? "contenteditable" : "textarea", function() {
7726 throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME
7728 option("rtlMoveVisually", !windows);
7729 option("wholeLineUpdateBefore", true);
7731 option("theme", "default", function(cm) {
7735 option("keyMap", "default", function(cm, val, old) {
7736 var next = getKeyMap(val);
7737 var prev = old != CodeMirror.Init && getKeyMap(old);
7738 if (prev && prev.detach) prev.detach(cm, next);
7739 if (next.attach) next.attach(cm, prev || null);
7741 option("extraKeys", null);
7743 option("lineWrapping", false, wrappingChanged, true);
7744 option("gutters", [], function(cm) {
7745 setGuttersForLineNumbers(cm.options);
7748 option("fixedGutter", true, function(cm, val) {
7749 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
7752 option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true);
7753 option("scrollbarStyle", "native", function(cm) {
7755 updateScrollbars(cm);
7756 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
7757 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
7759 option("lineNumbers", false, function(cm) {
7760 setGuttersForLineNumbers(cm.options);
7763 option("firstLineNumber", 1, guttersChanged, true);
7764 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
7765 option("showCursorWhenSelecting", false, updateSelection, true);
7767 option("resetSelectionOnContextMenu", true);
7768 option("lineWiseCopyCut", true);
7770 option("readOnly", false, function(cm, val) {
7771 if (val == "nocursor") {
7773 cm.display.input.blur();
7774 cm.display.disabled = true;
7776 cm.display.disabled = false;
7778 cm.display.input.readOnlyChanged(val)
7780 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true);
7781 option("dragDrop", true, dragDropChanged);
7782 option("allowDropFileTypes", null);
7784 option("cursorBlinkRate", 530);
7785 option("cursorScrollMargin", 0);
7786 option("cursorHeight", 1, updateSelection, true);
7787 option("singleCursorHeightPerLine", true, updateSelection, true);
7788 option("workTime", 100);
7789 option("workDelay", 100);
7790 option("flattenSpans", true, resetModeState, true);
7791 option("addModeClass", false, resetModeState, true);
7792 option("pollInterval", 100);
7793 option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;});
7794 option("historyEventDelay", 1250);
7795 option("viewportMargin", 10, function(cm){cm.refresh();}, true);
7796 option("maxHighlightLength", 10000, resetModeState, true);
7797 option("moveInputWithCursor", true, function(cm, val) {
7798 if (!val) cm.display.input.resetPosition();
7801 option("tabindex", null, function(cm, val) {
7802 cm.display.input.getField().tabIndex = val || "";
7804 option("autofocus", null);
7806 // MODE DEFINITION AND QUERYING
7808 // Known modes, by name and by MIME
7809 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
7811 // Extra arguments are stored as the mode's dependencies, which is
7812 // used by (legacy) mechanisms like loadmode.js to automatically
7813 // load a mode. (Preferred mechanism is the require/define calls.)
7814 CodeMirror.defineMode = function(name, mode) {
7815 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
7816 if (arguments.length > 2)
7817 mode.dependencies = Array.prototype.slice.call(arguments, 2);
7821 CodeMirror.defineMIME = function(mime, spec) {
7822 mimeModes[mime] = spec;
7825 // Given a MIME type, a {name, ...options} config object, or a name
7826 // string, return a mode config object.
7827 CodeMirror.resolveMode = function(spec) {
7828 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
7829 spec = mimeModes[spec];
7830 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
7831 var found = mimeModes[spec.name];
7832 if (typeof found == "string") found = {name: found};
7833 spec = createObj(found, spec);
7834 spec.name = found.name;
7835 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
7836 return CodeMirror.resolveMode("application/xml");
7838 if (typeof spec == "string") return {name: spec};
7839 else return spec || {name: "null"};
7842 // Given a mode spec (anything that resolveMode accepts), find and
7843 // initialize an actual mode object.
7844 CodeMirror.getMode = function(options, spec) {
7845 var spec = CodeMirror.resolveMode(spec);
7846 var mfactory = modes[spec.name];
7847 if (!mfactory) return CodeMirror.getMode(options, "text/plain");
7848 var modeObj = mfactory(options, spec);
7849 if (modeExtensions.hasOwnProperty(spec.name)) {
7850 var exts = modeExtensions[spec.name];
7851 for (var prop in exts) {
7852 if (!exts.hasOwnProperty(prop)) continue;
7853 if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
7854 modeObj[prop] = exts[prop];
7857 modeObj.name = spec.name;
7858 if (spec.helperType) modeObj.helperType = spec.helperType;
7859 if (spec.modeProps) for (var prop in spec.modeProps)
7860 modeObj[prop] = spec.modeProps[prop];
7865 // Minimal default mode.
7866 CodeMirror.defineMode("null", function() {
7867 return {token: function(stream) {stream.skipToEnd();}};
7869 CodeMirror.defineMIME("text/plain", "null");
7871 // This can be used to attach properties to mode objects from
7872 // outside the actual mode definition.
7873 var modeExtensions = CodeMirror.modeExtensions = {};
7874 CodeMirror.extendMode = function(mode, properties) {
7875 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
7876 copyObj(properties, exts);
7881 CodeMirror.defineExtension = function(name, func) {
7882 CodeMirror.prototype[name] = func;
7884 CodeMirror.defineDocExtension = function(name, func) {
7885 Doc.prototype[name] = func;
7887 CodeMirror.defineOption = option;
7890 CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
7892 var helpers = CodeMirror.helpers = {};
7893 CodeMirror.registerHelper = function(type, name, value) {
7894 if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
7895 helpers[type][name] = value;
7897 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
7898 CodeMirror.registerHelper(type, name, value);
7899 helpers[type]._global.push({pred: predicate, val: value});
7902 // MODE STATE HANDLING
7904 // Utility functions for working with state. Exported because nested
7905 // modes need to do this for their inner modes.
7907 var copyState = CodeMirror.copyState = function(mode, state) {
7908 if (state === true) return state;
7909 if (mode.copyState) return mode.copyState(state);
7911 for (var n in state) {
7913 if (val instanceof Array) val = val.concat([]);
7919 var startState = CodeMirror.startState = function(mode, a1, a2) {
7920 return mode.startState ? mode.startState(a1, a2) : true;
7923 // Given a mode and a state (for that mode), find the inner mode and
7924 // state at the position that the state refers to.
7925 CodeMirror.innerMode = function(mode, state) {
7926 while (mode.innerMode) {
7927 var info = mode.innerMode(state);
7928 if (!info || info.mode == mode) break;
7932 return info || {mode: mode, state: state};
7935 // STANDARD COMMANDS
7937 // Commands are parameter-less actions that can be performed on an
7938 // editor, mostly used for keybindings.
7939 var commands = CodeMirror.commands = {
7940 selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
7941 singleSelection: function(cm) {
7942 cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll);
7944 killLine: function(cm) {
7945 deleteNearSelection(cm, function(range) {
7946 if (range.empty()) {
7947 var len = getLine(cm.doc, range.head.line).text.length;
7948 if (range.head.ch == len && range.head.line < cm.lastLine())
7949 return {from: range.head, to: Pos(range.head.line + 1, 0)};
7951 return {from: range.head, to: Pos(range.head.line, len)};
7953 return {from: range.from(), to: range.to()};
7957 deleteLine: function(cm) {
7958 deleteNearSelection(cm, function(range) {
7959 return {from: Pos(range.from().line, 0),
7960 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
7963 delLineLeft: function(cm) {
7964 deleteNearSelection(cm, function(range) {
7965 return {from: Pos(range.from().line, 0), to: range.from()};
7968 delWrappedLineLeft: function(cm) {
7969 deleteNearSelection(cm, function(range) {
7970 var top = cm.charCoords(range.head, "div").top + 5;
7971 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
7972 return {from: leftPos, to: range.from()};
7975 delWrappedLineRight: function(cm) {
7976 deleteNearSelection(cm, function(range) {
7977 var top = cm.charCoords(range.head, "div").top + 5;
7978 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
7979 return {from: range.from(), to: rightPos };
7982 undo: function(cm) {cm.undo();},
7983 redo: function(cm) {cm.redo();},
7984 undoSelection: function(cm) {cm.undoSelection();},
7985 redoSelection: function(cm) {cm.redoSelection();},
7986 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
7987 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
7988 goLineStart: function(cm) {
7989 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); },
7990 {origin: "+move", bias: 1});
7992 goLineStartSmart: function(cm) {
7993 cm.extendSelectionsBy(function(range) {
7994 return lineStartSmart(cm, range.head);
7995 }, {origin: "+move", bias: 1});
7997 goLineEnd: function(cm) {
7998 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); },
7999 {origin: "+move", bias: -1});
8001 goLineRight: function(cm) {
8002 cm.extendSelectionsBy(function(range) {
8003 var top = cm.charCoords(range.head, "div").top + 5;
8004 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
8007 goLineLeft: function(cm) {
8008 cm.extendSelectionsBy(function(range) {
8009 var top = cm.charCoords(range.head, "div").top + 5;
8010 return cm.coordsChar({left: 0, top: top}, "div");
8013 goLineLeftSmart: function(cm) {
8014 cm.extendSelectionsBy(function(range) {
8015 var top = cm.charCoords(range.head, "div").top + 5;
8016 var pos = cm.coordsChar({left: 0, top: top}, "div");
8017 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head);
8021 goLineUp: function(cm) {cm.moveV(-1, "line");},
8022 goLineDown: function(cm) {cm.moveV(1, "line");},
8023 goPageUp: function(cm) {cm.moveV(-1, "page");},
8024 goPageDown: function(cm) {cm.moveV(1, "page");},
8025 goCharLeft: function(cm) {cm.moveH(-1, "char");},
8026 goCharRight: function(cm) {cm.moveH(1, "char");},
8027 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
8028 goColumnRight: function(cm) {cm.moveH(1, "column");},
8029 goWordLeft: function(cm) {cm.moveH(-1, "word");},
8030 goGroupRight: function(cm) {cm.moveH(1, "group");},
8031 goGroupLeft: function(cm) {cm.moveH(-1, "group");},
8032 goWordRight: function(cm) {cm.moveH(1, "word");},
8033 delCharBefore: function(cm) {cm.deleteH(-1, "char");},
8034 delCharAfter: function(cm) {cm.deleteH(1, "char");},
8035 delWordBefore: function(cm) {cm.deleteH(-1, "word");},
8036 delWordAfter: function(cm) {cm.deleteH(1, "word");},
8037 delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
8038 delGroupAfter: function(cm) {cm.deleteH(1, "group");},
8039 indentAuto: function(cm) {cm.indentSelection("smart");},
8040 indentMore: function(cm) {cm.indentSelection("add");},
8041 indentLess: function(cm) {cm.indentSelection("subtract");},
8042 insertTab: function(cm) {cm.replaceSelection("\t");},
8043 insertSoftTab: function(cm) {
8044 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
8045 for (var i = 0; i < ranges.length; i++) {
8046 var pos = ranges[i].from();
8047 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
8048 spaces.push(spaceStr(tabSize - col % tabSize));
8050 cm.replaceSelections(spaces);
8052 defaultTab: function(cm) {
8053 if (cm.somethingSelected()) cm.indentSelection("add");
8054 else cm.execCommand("insertTab");
8056 transposeChars: function(cm) {
8057 runInOp(cm, function() {
8058 var ranges = cm.listSelections(), newSel = [];
8059 for (var i = 0; i < ranges.length; i++) {
8060 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
8062 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1);
8064 cur = new Pos(cur.line, cur.ch + 1);
8065 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
8066 Pos(cur.line, cur.ch - 2), cur, "+transpose");
8067 } else if (cur.line > cm.doc.first) {
8068 var prev = getLine(cm.doc, cur.line - 1).text;
8070 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
8071 prev.charAt(prev.length - 1),
8072 Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose");
8075 newSel.push(new Range(cur, cur));
8077 cm.setSelections(newSel);
8080 newlineAndIndent: function(cm) {
8081 runInOp(cm, function() {
8082 var len = cm.listSelections().length;
8083 for (var i = 0; i < len; i++) {
8084 var range = cm.listSelections()[i];
8085 cm.replaceRange(cm.doc.lineSeparator(), range.anchor, range.head, "+input");
8086 cm.indentLine(range.from().line + 1, null, true);
8088 ensureCursorVisible(cm);
8091 openLine: function(cm) {cm.replaceSelection("\n", "start")},
8092 toggleOverwrite: function(cm) {cm.toggleOverwrite();}
8098 var keyMap = CodeMirror.keyMap = {};
8101 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
8102 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
8103 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
8104 "Tab": "defaultTab", "Shift-Tab": "indentAuto",
8105 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
8106 "Esc": "singleSelection"
8108 // Note that the save and find-related commands aren't defined by
8109 // default. User code or addons can define them. Unknown commands
8110 // are simply ignored.
8111 keyMap.pcDefault = {
8112 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
8113 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
8114 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
8115 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
8116 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
8117 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
8118 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
8119 fallthrough: "basic"
8121 // Very basic readline/emacs-style bindings, which are standard on Mac.
8123 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
8124 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
8125 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
8126 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
8127 "Ctrl-O": "openLine"
8129 keyMap.macDefault = {
8130 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
8131 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
8132 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
8133 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
8134 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
8135 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
8136 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
8137 fallthrough: ["basic", "emacsy"]
8139 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
8143 function normalizeKeyName(name) {
8144 var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
8145 var alt, ctrl, shift, cmd;
8146 for (var i = 0; i < parts.length - 1; i++) {
8148 if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
8149 else if (/^a(lt)?$/i.test(mod)) alt = true;
8150 else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
8151 else if (/^s(hift)$/i.test(mod)) shift = true;
8152 else throw new Error("Unrecognized modifier name: " + mod);
8154 if (alt) name = "Alt-" + name;
8155 if (ctrl) name = "Ctrl-" + name;
8156 if (cmd) name = "Cmd-" + name;
8157 if (shift) name = "Shift-" + name;
8161 // This is a kludge to keep keymaps mostly working as raw objects
8162 // (backwards compatibility) while at the same time support features
8163 // like normalization and multi-stroke key bindings. It compiles a
8164 // new normalized keymap, and then updates the old object to reflect
8166 CodeMirror.normalizeKeyMap = function(keymap) {
8168 for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
8169 var value = keymap[keyname];
8170 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
8171 if (value == "...") { delete keymap[keyname]; continue; }
8173 var keys = map(keyname.split(" "), normalizeKeyName);
8174 for (var i = 0; i < keys.length; i++) {
8176 if (i == keys.length - 1) {
8177 name = keys.join(" ");
8180 name = keys.slice(0, i + 1).join(" ");
8183 var prev = copy[name];
8184 if (!prev) copy[name] = val;
8185 else if (prev != val) throw new Error("Inconsistent bindings for " + name);
8187 delete keymap[keyname];
8189 for (var prop in copy) keymap[prop] = copy[prop];
8193 var lookupKey = CodeMirror.lookupKey = function(key, map, handle, context) {
8194 map = getKeyMap(map);
8195 var found = map.call ? map.call(key, context) : map[key];
8196 if (found === false) return "nothing";
8197 if (found === "...") return "multi";
8198 if (found != null && handle(found)) return "handled";
8200 if (map.fallthrough) {
8201 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
8202 return lookupKey(key, map.fallthrough, handle, context);
8203 for (var i = 0; i < map.fallthrough.length; i++) {
8204 var result = lookupKey(key, map.fallthrough[i], handle, context);
8205 if (result) return result;
8210 // Modifier key presses don't count as 'real' key presses for the
8211 // purpose of keymap fallthrough.
8212 var isModifierKey = CodeMirror.isModifierKey = function(value) {
8213 var name = typeof value == "string" ? value : keyNames[value.keyCode];
8214 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
8217 // Look up the name of a key as indicated by an event object.
8218 var keyName = CodeMirror.keyName = function(event, noShift) {
8219 if (presto && event.keyCode == 34 && event["char"]) return false;
8220 var base = keyNames[event.keyCode], name = base;
8221 if (name == null || event.altGraphKey) return false;
8222 if (event.altKey && base != "Alt") name = "Alt-" + name;
8223 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
8224 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
8225 if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
8229 function getKeyMap(val) {
8230 return typeof val == "string" ? keyMap[val] : val;
8235 CodeMirror.fromTextArea = function(textarea, options) {
8236 options = options ? copyObj(options) : {};
8237 options.value = textarea.value;
8238 if (!options.tabindex && textarea.tabIndex)
8239 options.tabindex = textarea.tabIndex;
8240 if (!options.placeholder && textarea.placeholder)
8241 options.placeholder = textarea.placeholder;
8242 // Set autofocus to true if this textarea is focused, or if it has
8243 // autofocus and no other element is focused.
8244 if (options.autofocus == null) {
8245 var hasFocus = activeElt();
8246 options.autofocus = hasFocus == textarea ||
8247 textarea.getAttribute("autofocus") != null && hasFocus == document.body;
8250 function save() {textarea.value = cm.getValue();}
8251 if (textarea.form) {
8252 on(textarea.form, "submit", save);
8253 // Deplorable hack to make the submit method do the right thing.
8254 if (!options.leaveSubmitMethodAlone) {
8255 var form = textarea.form, realSubmit = form.submit;
8257 var wrappedSubmit = form.submit = function() {
8259 form.submit = realSubmit;
8261 form.submit = wrappedSubmit;
8267 options.finishInit = function(cm) {
8269 cm.getTextArea = function() { return textarea; };
8270 cm.toTextArea = function() {
8271 cm.toTextArea = isNaN; // Prevent this from being ran twice
8273 textarea.parentNode.removeChild(cm.getWrapperElement());
8274 textarea.style.display = "";
8275 if (textarea.form) {
8276 off(textarea.form, "submit", save);
8277 if (typeof textarea.form.submit == "function")
8278 textarea.form.submit = realSubmit;
8283 textarea.style.display = "none";
8284 var cm = CodeMirror(function(node) {
8285 textarea.parentNode.insertBefore(node, textarea.nextSibling);
8292 // Fed to the mode parsers, provides helper functions to make
8293 // parsers more succinct.
8295 var StringStream = CodeMirror.StringStream = function(string, tabSize) {
8296 this.pos = this.start = 0;
8297 this.string = string;
8298 this.tabSize = tabSize || 8;
8299 this.lastColumnPos = this.lastColumnValue = 0;
8303 StringStream.prototype = {
8304 eol: function() {return this.pos >= this.string.length;},
8305 sol: function() {return this.pos == this.lineStart;},
8306 peek: function() {return this.string.charAt(this.pos) || undefined;},
8308 if (this.pos < this.string.length)
8309 return this.string.charAt(this.pos++);
8311 eat: function(match) {
8312 var ch = this.string.charAt(this.pos);
8313 if (typeof match == "string") var ok = ch == match;
8314 else var ok = ch && (match.test ? match.test(ch) : match(ch));
8315 if (ok) {++this.pos; return ch;}
8317 eatWhile: function(match) {
8318 var start = this.pos;
8319 while (this.eat(match)){}
8320 return this.pos > start;
8322 eatSpace: function() {
8323 var start = this.pos;
8324 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
8325 return this.pos > start;
8327 skipToEnd: function() {this.pos = this.string.length;},
8328 skipTo: function(ch) {
8329 var found = this.string.indexOf(ch, this.pos);
8330 if (found > -1) {this.pos = found; return true;}
8332 backUp: function(n) {this.pos -= n;},
8333 column: function() {
8334 if (this.lastColumnPos < this.start) {
8335 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
8336 this.lastColumnPos = this.start;
8338 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
8340 indentation: function() {
8341 return countColumn(this.string, null, this.tabSize) -
8342 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
8344 match: function(pattern, consume, caseInsensitive) {
8345 if (typeof pattern == "string") {
8346 var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
8347 var substr = this.string.substr(this.pos, pattern.length);
8348 if (cased(substr) == cased(pattern)) {
8349 if (consume !== false) this.pos += pattern.length;
8353 var match = this.string.slice(this.pos).match(pattern);
8354 if (match && match.index > 0) return null;
8355 if (match && consume !== false) this.pos += match[0].length;
8359 current: function(){return this.string.slice(this.start, this.pos);},
8360 hideFirstChars: function(n, inner) {
8361 this.lineStart += n;
8362 try { return inner(); }
8363 finally { this.lineStart -= n; }
8369 // Created with markText and setBookmark methods. A TextMarker is a
8370 // handle that can be used to clear or find a marked position in the
8371 // document. Line objects hold arrays (markedSpans) containing
8372 // {from, to, marker} object pointing to such marker objects, and
8373 // indicating that such a marker is present on that line. Multiple
8374 // lines may point to the same marker when it spans across lines.
8375 // The spans will have null for their from/to properties when the
8376 // marker continues beyond the start/end of the line. Markers have
8377 // links back to the lines they currently touch.
8379 var nextMarkerId = 0;
8381 var TextMarker = CodeMirror.TextMarker = function(doc, type) {
8385 this.id = ++nextMarkerId;
8387 eventMixin(TextMarker);
8389 // Clear the marker.
8390 TextMarker.prototype.clear = function() {
8391 if (this.explicitlyCleared) return;
8392 var cm = this.doc.cm, withOp = cm && !cm.curOp;
8393 if (withOp) startOperation(cm);
8394 if (hasHandler(this, "clear")) {
8395 var found = this.find();
8396 if (found) signalLater(this, "clear", found.from, found.to);
8398 var min = null, max = null;
8399 for (var i = 0; i < this.lines.length; ++i) {
8400 var line = this.lines[i];
8401 var span = getMarkedSpanFor(line.markedSpans, this);
8402 if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text");
8404 if (span.to != null) max = lineNo(line);
8405 if (span.from != null) min = lineNo(line);
8407 line.markedSpans = removeMarkedSpan(line.markedSpans, span);
8408 if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
8409 updateLineHeight(line, textHeight(cm.display));
8411 if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
8412 var visual = visualLine(this.lines[i]), len = lineLength(visual);
8413 if (len > cm.display.maxLineLength) {
8414 cm.display.maxLine = visual;
8415 cm.display.maxLineLength = len;
8416 cm.display.maxLineChanged = true;
8420 if (min != null && cm && this.collapsed) regChange(cm, min, max + 1);
8421 this.lines.length = 0;
8422 this.explicitlyCleared = true;
8423 if (this.atomic && this.doc.cantEdit) {
8424 this.doc.cantEdit = false;
8425 if (cm) reCheckSelection(cm.doc);
8427 if (cm) signalLater(cm, "markerCleared", cm, this);
8428 if (withOp) endOperation(cm);
8429 if (this.parent) this.parent.clear();
8432 // Find the position of the marker in the document. Returns a {from,
8433 // to} object by default. Side can be passed to get a specific side
8434 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
8435 // Pos objects returned contain a line object, rather than a line
8436 // number (used to prevent looking up the same line twice).
8437 TextMarker.prototype.find = function(side, lineObj) {
8438 if (side == null && this.type == "bookmark") side = 1;
8440 for (var i = 0; i < this.lines.length; ++i) {
8441 var line = this.lines[i];
8442 var span = getMarkedSpanFor(line.markedSpans, this);
8443 if (span.from != null) {
8444 from = Pos(lineObj ? line : lineNo(line), span.from);
8445 if (side == -1) return from;
8447 if (span.to != null) {
8448 to = Pos(lineObj ? line : lineNo(line), span.to);
8449 if (side == 1) return to;
8452 return from && {from: from, to: to};
8455 // Signals that the marker's widget changed, and surrounding layout
8456 // should be recomputed.
8457 TextMarker.prototype.changed = function() {
8458 var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
8459 if (!pos || !cm) return;
8460 runInOp(cm, function() {
8461 var line = pos.line, lineN = lineNo(pos.line);
8462 var view = findViewForLine(cm, lineN);
8464 clearLineMeasurementCacheFor(view);
8465 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
8467 cm.curOp.updateMaxLine = true;
8468 if (!lineIsHidden(widget.doc, line) && widget.height != null) {
8469 var oldHeight = widget.height;
8470 widget.height = null;
8471 var dHeight = widgetHeight(widget) - oldHeight;
8473 updateLineHeight(line, line.height + dHeight);
8478 TextMarker.prototype.attachLine = function(line) {
8479 if (!this.lines.length && this.doc.cm) {
8480 var op = this.doc.cm.curOp;
8481 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
8482 (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
8484 this.lines.push(line);
8486 TextMarker.prototype.detachLine = function(line) {
8487 this.lines.splice(indexOf(this.lines, line), 1);
8488 if (!this.lines.length && this.doc.cm) {
8489 var op = this.doc.cm.curOp;
8490 (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
8494 // Collapsed markers have unique ids, in order to be able to order
8495 // them, which is needed for uniquely determining an outer marker
8496 // when they overlap (they may nest, but not partially overlap).
8497 var nextMarkerId = 0;
8499 // Create a marker, wire it up to the right lines, and
8500 function markText(doc, from, to, options, type) {
8501 // Shared markers (across linked documents) are handled separately
8502 // (markTextShared will call out to this again, once per
8504 if (options && options.shared) return markTextShared(doc, from, to, options, type);
8505 // Ensure we are in an operation.
8506 if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
8508 var marker = new TextMarker(doc, type), diff = cmp(from, to);
8509 if (options) copyObj(options, marker, false);
8510 // Don't connect empty markers unless clearWhenEmpty is false
8511 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
8513 if (marker.replacedWith) {
8514 // Showing up as a widget implies collapsed (widget replaces text)
8515 marker.collapsed = true;
8516 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget");
8517 if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true");
8518 if (options.insertLeft) marker.widgetNode.insertLeft = true;
8520 if (marker.collapsed) {
8521 if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
8522 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
8523 throw new Error("Inserting collapsed marker partially overlapping an existing one");
8524 sawCollapsedSpans = true;
8527 if (marker.addToHistory)
8528 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN);
8530 var curLine = from.line, cm = doc.cm, updateMaxLine;
8531 doc.iter(curLine, to.line + 1, function(line) {
8532 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
8533 updateMaxLine = true;
8534 if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0);
8535 addMarkedSpan(line, new MarkedSpan(marker,
8536 curLine == from.line ? from.ch : null,
8537 curLine == to.line ? to.ch : null));
8540 // lineIsHidden depends on the presence of the spans, so needs a second pass
8541 if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
8542 if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
8545 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
8547 if (marker.readOnly) {
8548 sawReadOnlySpans = true;
8549 if (doc.history.done.length || doc.history.undone.length)
8552 if (marker.collapsed) {
8553 marker.id = ++nextMarkerId;
8554 marker.atomic = true;
8557 // Sync editor state
8558 if (updateMaxLine) cm.curOp.updateMaxLine = true;
8559 if (marker.collapsed)
8560 regChange(cm, from.line, to.line + 1);
8561 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css)
8562 for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text");
8563 if (marker.atomic) reCheckSelection(cm.doc);
8564 signalLater(cm, "markerAdded", cm, marker);
8569 // SHARED TEXTMARKERS
8571 // A shared marker spans multiple linked documents. It is
8572 // implemented as a meta-marker-object controlling multiple normal
8574 var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
8575 this.markers = markers;
8576 this.primary = primary;
8577 for (var i = 0; i < markers.length; ++i)
8578 markers[i].parent = this;
8580 eventMixin(SharedTextMarker);
8582 SharedTextMarker.prototype.clear = function() {
8583 if (this.explicitlyCleared) return;
8584 this.explicitlyCleared = true;
8585 for (var i = 0; i < this.markers.length; ++i)
8586 this.markers[i].clear();
8587 signalLater(this, "clear");
8589 SharedTextMarker.prototype.find = function(side, lineObj) {
8590 return this.primary.find(side, lineObj);
8593 function markTextShared(doc, from, to, options, type) {
8594 options = copyObj(options);
8595 options.shared = false;
8596 var markers = [markText(doc, from, to, options, type)], primary = markers[0];
8597 var widget = options.widgetNode;
8598 linkedDocs(doc, function(doc) {
8599 if (widget) options.widgetNode = widget.cloneNode(true);
8600 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
8601 for (var i = 0; i < doc.linked.length; ++i)
8602 if (doc.linked[i].isParent) return;
8603 primary = lst(markers);
8605 return new SharedTextMarker(markers, primary);
8608 function findSharedMarkers(doc) {
8609 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
8610 function(m) { return m.parent; });
8613 function copySharedMarkers(doc, markers) {
8614 for (var i = 0; i < markers.length; i++) {
8615 var marker = markers[i], pos = marker.find();
8616 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
8617 if (cmp(mFrom, mTo)) {
8618 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
8619 marker.markers.push(subMark);
8620 subMark.parent = marker;
8625 function detachSharedMarkers(markers) {
8626 for (var i = 0; i < markers.length; i++) {
8627 var marker = markers[i], linked = [marker.primary.doc];;
8628 linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
8629 for (var j = 0; j < marker.markers.length; j++) {
8630 var subMarker = marker.markers[j];
8631 if (indexOf(linked, subMarker.doc) == -1) {
8632 subMarker.parent = null;
8633 marker.markers.splice(j--, 1);
8641 function MarkedSpan(marker, from, to) {
8642 this.marker = marker;
8643 this.from = from; this.to = to;
8646 // Search an array of spans for a span matching the given marker.
8647 function getMarkedSpanFor(spans, marker) {
8648 if (spans) for (var i = 0; i < spans.length; ++i) {
8649 var span = spans[i];
8650 if (span.marker == marker) return span;
8653 // Remove a span from an array, returning undefined if no spans are
8654 // left (we don't store arrays for lines without spans).
8655 function removeMarkedSpan(spans, span) {
8656 for (var r, i = 0; i < spans.length; ++i)
8657 if (spans[i] != span) (r || (r = [])).push(spans[i]);
8660 // Add a span to a line.
8661 function addMarkedSpan(line, span) {
8662 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
8663 span.marker.attachLine(line);
8666 // Used for the algorithm that adjusts markers for a change in the
8667 // document. These functions cut an array of spans at a given
8668 // character position, returning an array of remaining chunks (or
8669 // undefined if nothing remains).
8670 function markedSpansBefore(old, startCh, isInsert) {
8671 if (old) for (var i = 0, nw; i < old.length; ++i) {
8672 var span = old[i], marker = span.marker;
8673 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
8674 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
8675 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
8676 (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
8681 function markedSpansAfter(old, endCh, isInsert) {
8682 if (old) for (var i = 0, nw; i < old.length; ++i) {
8683 var span = old[i], marker = span.marker;
8684 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
8685 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
8686 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
8687 (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
8688 span.to == null ? null : span.to - endCh));
8694 // Given a change object, compute the new set of marker spans that
8695 // cover the line in which the change took place. Removes spans
8696 // entirely within the change, reconnects spans belonging to the
8697 // same marker that appear on both sides of the change, and cuts off
8698 // spans partially within the change. Returns an array of span
8699 // arrays with one element for each line in (after) the change.
8700 function stretchSpansOverChange(doc, change) {
8701 if (change.full) return null;
8702 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
8703 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
8704 if (!oldFirst && !oldLast) return null;
8706 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
8707 // Get the spans that 'stick out' on both sides
8708 var first = markedSpansBefore(oldFirst, startCh, isInsert);
8709 var last = markedSpansAfter(oldLast, endCh, isInsert);
8711 // Next, merge those two ends
8712 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
8714 // Fix up .to properties of first
8715 for (var i = 0; i < first.length; ++i) {
8716 var span = first[i];
8717 if (span.to == null) {
8718 var found = getMarkedSpanFor(last, span.marker);
8719 if (!found) span.to = startCh;
8720 else if (sameLine) span.to = found.to == null ? null : found.to + offset;
8725 // Fix up .from in last (or move them into first in case of sameLine)
8726 for (var i = 0; i < last.length; ++i) {
8728 if (span.to != null) span.to += offset;
8729 if (span.from == null) {
8730 var found = getMarkedSpanFor(first, span.marker);
8733 if (sameLine) (first || (first = [])).push(span);
8736 span.from += offset;
8737 if (sameLine) (first || (first = [])).push(span);
8741 // Make sure we didn't create any zero-length spans
8742 if (first) first = clearEmptySpans(first);
8743 if (last && last != first) last = clearEmptySpans(last);
8745 var newMarkers = [first];
8747 // Fill gap with whole-line-spans
8748 var gap = change.text.length - 2, gapMarkers;
8749 if (gap > 0 && first)
8750 for (var i = 0; i < first.length; ++i)
8751 if (first[i].to == null)
8752 (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
8753 for (var i = 0; i < gap; ++i)
8754 newMarkers.push(gapMarkers);
8755 newMarkers.push(last);
8760 // Remove spans that are empty and don't have a clearWhenEmpty
8762 function clearEmptySpans(spans) {
8763 for (var i = 0; i < spans.length; ++i) {
8764 var span = spans[i];
8765 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
8766 spans.splice(i--, 1);
8768 if (!spans.length) return null;
8772 // Used for un/re-doing changes from the history. Combines the
8773 // result of computing the existing spans with the set of spans that
8774 // existed in the history (so that deleting around a span and then
8775 // undoing brings back the span).
8776 function mergeOldSpans(doc, change) {
8777 var old = getOldSpans(doc, change);
8778 var stretched = stretchSpansOverChange(doc, change);
8779 if (!old) return stretched;
8780 if (!stretched) return old;
8782 for (var i = 0; i < old.length; ++i) {
8783 var oldCur = old[i], stretchCur = stretched[i];
8784 if (oldCur && stretchCur) {
8785 spans: for (var j = 0; j < stretchCur.length; ++j) {
8786 var span = stretchCur[j];
8787 for (var k = 0; k < oldCur.length; ++k)
8788 if (oldCur[k].marker == span.marker) continue spans;
8791 } else if (stretchCur) {
8792 old[i] = stretchCur;
8798 // Used to 'clip' out readOnly ranges when making a change.
8799 function removeReadOnlyRanges(doc, from, to) {
8801 doc.iter(from.line, to.line + 1, function(line) {
8802 if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
8803 var mark = line.markedSpans[i].marker;
8804 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
8805 (markers || (markers = [])).push(mark);
8808 if (!markers) return null;
8809 var parts = [{from: from, to: to}];
8810 for (var i = 0; i < markers.length; ++i) {
8811 var mk = markers[i], m = mk.find(0);
8812 for (var j = 0; j < parts.length; ++j) {
8814 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue;
8815 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
8816 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
8817 newParts.push({from: p.from, to: m.from});
8818 if (dto > 0 || !mk.inclusiveRight && !dto)
8819 newParts.push({from: m.to, to: p.to});
8820 parts.splice.apply(parts, newParts);
8821 j += newParts.length - 1;
8827 // Connect or disconnect spans from a line.
8828 function detachMarkedSpans(line) {
8829 var spans = line.markedSpans;
8831 for (var i = 0; i < spans.length; ++i)
8832 spans[i].marker.detachLine(line);
8833 line.markedSpans = null;
8835 function attachMarkedSpans(line, spans) {
8837 for (var i = 0; i < spans.length; ++i)
8838 spans[i].marker.attachLine(line);
8839 line.markedSpans = spans;
8842 // Helpers used when computing which overlapping collapsed span
8843 // counts as the larger one.
8844 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
8845 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
8847 // Returns a number indicating which of two overlapping collapsed
8848 // spans is larger (and thus includes the other). Falls back to
8849 // comparing ids when the spans cover exactly the same range.
8850 function compareCollapsedMarkers(a, b) {
8851 var lenDiff = a.lines.length - b.lines.length;
8852 if (lenDiff != 0) return lenDiff;
8853 var aPos = a.find(), bPos = b.find();
8854 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
8855 if (fromCmp) return -fromCmp;
8856 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
8857 if (toCmp) return toCmp;
8861 // Find out whether a line ends or starts in a collapsed span. If
8862 // so, return the marker for that span.
8863 function collapsedSpanAtSide(line, start) {
8864 var sps = sawCollapsedSpans && line.markedSpans, found;
8865 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
8867 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
8868 (!found || compareCollapsedMarkers(found, sp.marker) < 0))
8873 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
8874 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
8876 // Test whether there exists a collapsed span that partially
8877 // overlaps (covers the start or end, but not both) of a new span.
8878 // Such overlap is not allowed.
8879 function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
8880 var line = getLine(doc, lineNo);
8881 var sps = sawCollapsedSpans && line.markedSpans;
8882 if (sps) for (var i = 0; i < sps.length; ++i) {
8884 if (!sp.marker.collapsed) continue;
8885 var found = sp.marker.find(0);
8886 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
8887 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
8888 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue;
8889 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
8890 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
8895 // A visual line is a line as drawn on the screen. Folding, for
8896 // example, can cause multiple logical lines to appear on the same
8897 // visual line. This finds the start of the visual line that the
8898 // given line is part of (usually that is the line itself).
8899 function visualLine(line) {
8901 while (merged = collapsedSpanAtStart(line))
8902 line = merged.find(-1, true).line;
8906 // Returns an array of logical lines that continue the visual line
8907 // started by the argument, or undefined if there are no such lines.
8908 function visualLineContinued(line) {
8910 while (merged = collapsedSpanAtEnd(line)) {
8911 line = merged.find(1, true).line;
8912 (lines || (lines = [])).push(line);
8917 // Get the line number of the start of the visual line that the
8918 // given line number is part of.
8919 function visualLineNo(doc, lineN) {
8920 var line = getLine(doc, lineN), vis = visualLine(line);
8921 if (line == vis) return lineN;
8924 // Get the line number of the start of the next visual line after
8926 function visualLineEndNo(doc, lineN) {
8927 if (lineN > doc.lastLine()) return lineN;
8928 var line = getLine(doc, lineN), merged;
8929 if (!lineIsHidden(doc, line)) return lineN;
8930 while (merged = collapsedSpanAtEnd(line))
8931 line = merged.find(1, true).line;
8932 return lineNo(line) + 1;
8935 // Compute whether a line is hidden. Lines count as hidden when they
8936 // are part of a visual line that starts with another line, or when
8937 // they are entirely covered by collapsed, non-widget span.
8938 function lineIsHidden(doc, line) {
8939 var sps = sawCollapsedSpans && line.markedSpans;
8940 if (sps) for (var sp, i = 0; i < sps.length; ++i) {
8942 if (!sp.marker.collapsed) continue;
8943 if (sp.from == null) return true;
8944 if (sp.marker.widgetNode) continue;
8945 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
8949 function lineIsHiddenInner(doc, line, span) {
8950 if (span.to == null) {
8951 var end = span.marker.find(1, true);
8952 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
8954 if (span.marker.inclusiveRight && span.to == line.text.length)
8956 for (var sp, i = 0; i < line.markedSpans.length; ++i) {
8957 sp = line.markedSpans[i];
8958 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
8959 (sp.to == null || sp.to != span.from) &&
8960 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
8961 lineIsHiddenInner(doc, line, sp)) return true;
8967 // Line widgets are block elements displayed above or below a line.
8969 var LineWidget = CodeMirror.LineWidget = function(doc, node, options) {
8970 if (options) for (var opt in options) if (options.hasOwnProperty(opt))
8971 this[opt] = options[opt];
8975 eventMixin(LineWidget);
8977 function adjustScrollWhenAboveVisible(cm, line, diff) {
8978 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
8979 addToScrollPos(cm, null, diff);
8982 LineWidget.prototype.clear = function() {
8983 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
8984 if (no == null || !ws) return;
8985 for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
8986 if (!ws.length) line.widgets = null;
8987 var height = widgetHeight(this);
8988 updateLineHeight(line, Math.max(0, line.height - height));
8989 if (cm) runInOp(cm, function() {
8990 adjustScrollWhenAboveVisible(cm, line, -height);
8991 regLineChange(cm, no, "widget");
8994 LineWidget.prototype.changed = function() {
8995 var oldH = this.height, cm = this.doc.cm, line = this.line;
8997 var diff = widgetHeight(this) - oldH;
8999 updateLineHeight(line, line.height + diff);
9000 if (cm) runInOp(cm, function() {
9001 cm.curOp.forceUpdate = true;
9002 adjustScrollWhenAboveVisible(cm, line, diff);
9006 function widgetHeight(widget) {
9007 if (widget.height != null) return widget.height;
9008 var cm = widget.doc.cm;
9010 if (!contains(document.body, widget.node)) {
9011 var parentStyle = "position: relative;";
9012 if (widget.coverGutter)
9013 parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;";
9014 if (widget.noHScroll)
9015 parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;";
9016 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
9018 return widget.height = widget.node.parentNode.offsetHeight;
9021 function addLineWidget(doc, handle, node, options) {
9022 var widget = new LineWidget(doc, node, options);
9024 if (cm && widget.noHScroll) cm.display.alignWidgets = true;
9025 changeLine(doc, handle, "widget", function(line) {
9026 var widgets = line.widgets || (line.widgets = []);
9027 if (widget.insertAt == null) widgets.push(widget);
9028 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
9030 if (cm && !lineIsHidden(doc, line)) {
9031 var aboveVisible = heightAtLine(line) < doc.scrollTop;
9032 updateLineHeight(line, line.height + widgetHeight(widget));
9033 if (aboveVisible) addToScrollPos(cm, null, widget.height);
9034 cm.curOp.forceUpdate = true;
9041 // LINE DATA STRUCTURE
9043 // Line objects. These hold state related to a line, including
9044 // highlighting info (the styles array).
9045 var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) {
9047 attachMarkedSpans(this, markedSpans);
9048 this.height = estimateHeight ? estimateHeight(this) : 1;
9051 Line.prototype.lineNo = function() { return lineNo(this); };
9053 // Change the content (text, markers) of a line. Automatically
9054 // invalidates cached information and tries to re-estimate the
9056 function updateLine(line, text, markedSpans, estimateHeight) {
9058 if (line.stateAfter) line.stateAfter = null;
9059 if (line.styles) line.styles = null;
9060 if (line.order != null) line.order = null;
9061 detachMarkedSpans(line);
9062 attachMarkedSpans(line, markedSpans);
9063 var estHeight = estimateHeight ? estimateHeight(line) : 1;
9064 if (estHeight != line.height) updateLineHeight(line, estHeight);
9067 // Detach a line from the document tree and its markers.
9068 function cleanUpLine(line) {
9070 detachMarkedSpans(line);
9073 function extractLineClasses(type, output) {
9074 if (type) for (;;) {
9075 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
9076 if (!lineClass) break;
9077 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
9078 var prop = lineClass[1] ? "bgClass" : "textClass";
9079 if (output[prop] == null)
9080 output[prop] = lineClass[2];
9081 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop]))
9082 output[prop] += " " + lineClass[2];
9087 function callBlankLine(mode, state) {
9088 if (mode.blankLine) return mode.blankLine(state);
9089 if (!mode.innerMode) return;
9090 var inner = CodeMirror.innerMode(mode, state);
9091 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
9094 function readToken(mode, stream, state, inner) {
9095 for (var i = 0; i < 10; i++) {
9096 if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
9097 var style = mode.token(stream, state);
9098 if (stream.pos > stream.start) return style;
9100 throw new Error("Mode " + mode.name + " failed to advance stream.");
9103 // Utility for getTokenAt and getLineTokens
9104 function takeToken(cm, pos, precise, asArray) {
9105 function getObj(copy) {
9106 return {start: stream.start, end: stream.pos,
9107 string: stream.current(),
9108 type: style || null,
9109 state: copy ? copyState(doc.mode, state) : state};
9112 var doc = cm.doc, mode = doc.mode, style;
9113 pos = clipPos(doc, pos);
9114 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
9115 var stream = new StringStream(line.text, cm.options.tabSize), tokens;
9116 if (asArray) tokens = [];
9117 while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
9118 stream.start = stream.pos;
9119 style = readToken(mode, stream, state);
9120 if (asArray) tokens.push(getObj(true));
9122 return asArray ? tokens : getObj();
9125 // Run the given mode's parser over a line, calling f for each token.
9126 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
9127 var flattenSpans = mode.flattenSpans;
9128 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
9129 var curStart = 0, curStyle = null;
9130 var stream = new StringStream(text, cm.options.tabSize), style;
9131 var inner = cm.options.addModeClass && [null];
9132 if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
9133 while (!stream.eol()) {
9134 if (stream.pos > cm.options.maxHighlightLength) {
9135 flattenSpans = false;
9136 if (forceToEnd) processLine(cm, text, state, stream.pos);
9137 stream.pos = text.length;
9140 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
9143 var mName = inner[0].name;
9144 if (mName) style = "m-" + (style ? mName + " " + style : mName);
9146 if (!flattenSpans || curStyle != style) {
9147 while (curStart < stream.start) {
9148 curStart = Math.min(stream.start, curStart + 50000);
9149 f(curStart, curStyle);
9153 stream.start = stream.pos;
9155 while (curStart < stream.pos) {
9156 // Webkit seems to refuse to render text nodes longer than 57444 characters
9157 var pos = Math.min(stream.pos, curStart + 50000);
9163 // Compute a style array (an array starting with a mode generation
9164 // -- for invalidation -- followed by pairs of end positions and
9165 // style strings), which is used to highlight the tokens on the
9167 function highlightLine(cm, line, state, forceToEnd) {
9168 // A styles array always starts with a number identifying the
9169 // mode/overlays that it is based on (for easy invalidation).
9170 var st = [cm.state.modeGen], lineClasses = {};
9171 // Compute the base array of styles
9172 runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
9173 st.push(end, style);
9174 }, lineClasses, forceToEnd);
9176 // Run overlays, adjust style array.
9177 for (var o = 0; o < cm.state.overlays.length; ++o) {
9178 var overlay = cm.state.overlays[o], i = 1, at = 0;
9179 runMode(cm, line.text, overlay.mode, true, function(end, style) {
9181 // Ensure there's a token end at the current position, and that i points at it
9185 st.splice(i, 1, end, st[i+1], i_end);
9187 at = Math.min(end, i_end);
9190 if (overlay.opaque) {
9191 st.splice(start, i - start, end, "cm-overlay " + style);
9194 for (; start < i; start += 2) {
9195 var cur = st[start+1];
9196 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style;
9202 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
9205 function getLineStyles(cm, line, updateFrontier) {
9206 if (!line.styles || line.styles[0] != cm.state.modeGen) {
9207 var state = getStateBefore(cm, lineNo(line));
9208 var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state);
9209 line.stateAfter = state;
9210 line.styles = result.styles;
9211 if (result.classes) line.styleClasses = result.classes;
9212 else if (line.styleClasses) line.styleClasses = null;
9213 if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
9218 // Lightweight form of highlight -- proceed over this line and
9219 // update state, but don't save a style array. Used for lines that
9220 // aren't currently visible.
9221 function processLine(cm, text, state, startAt) {
9222 var mode = cm.doc.mode;
9223 var stream = new StringStream(text, cm.options.tabSize);
9224 stream.start = stream.pos = startAt || 0;
9225 if (text == "") callBlankLine(mode, state);
9226 while (!stream.eol()) {
9227 readToken(mode, stream, state);
9228 stream.start = stream.pos;
9232 // Convert a style as returned by a mode (either null, or a string
9233 // containing one or more styles) to a CSS style. This is cached,
9234 // and also looks for line-wide styles.
9235 var styleToClassCache = {}, styleToClassCacheWithMode = {};
9236 function interpretTokenStyle(style, options) {
9237 if (!style || /^\s*$/.test(style)) return null;
9238 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
9239 return cache[style] ||
9240 (cache[style] = style.replace(/\S+/g, "cm-$&"));
9243 // Render the DOM representation of the text of a line. Also builds
9244 // up a 'line map', which points at the DOM nodes that represent
9245 // specific stretches of text, and is used by the measuring code.
9246 // The returned object contains the DOM node, this map, and
9247 // information about line-wide styles that were set by the mode.
9248 function buildLineContent(cm, lineView) {
9249 // The padding-right forces the element to have a 'border', which
9250 // is needed on Webkit to be able to get line-level bounding
9251 // rectangles for it (in measureChar).
9252 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null);
9253 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content,
9254 col: 0, pos: 0, cm: cm,
9255 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")};
9256 lineView.measure = {};
9258 // Iterate over the logical lines that make up this visual line.
9259 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
9260 var line = i ? lineView.rest[i - 1] : lineView.line, order;
9262 builder.addToken = buildToken;
9263 // Optionally wire in some hacks into the token-rendering
9264 // algorithm, to deal with browser quirks.
9265 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
9266 builder.addToken = buildTokenBadBidi(builder.addToken, order);
9268 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
9269 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
9270 if (line.styleClasses) {
9271 if (line.styleClasses.bgClass)
9272 builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
9273 if (line.styleClasses.textClass)
9274 builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "");
9277 // Ensure at least a single node is present, for measuring.
9278 if (builder.map.length == 0)
9279 builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
9281 // Store the map and a cache object for the current logical line
9283 lineView.measure.map = builder.map;
9284 lineView.measure.cache = {};
9286 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
9287 (lineView.measure.caches || (lineView.measure.caches = [])).push({});
9293 var last = builder.content.lastChild
9294 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
9295 builder.content.className = "cm-tab-wrap-hack";
9298 signal(cm, "renderLine", cm, lineView.line, builder.pre);
9299 if (builder.pre.className)
9300 builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
9305 function defaultSpecialCharPlaceholder(ch) {
9306 var token = elt("span", "\u2022", "cm-invalidchar");
9307 token.title = "\\u" + ch.charCodeAt(0).toString(16);
9308 token.setAttribute("aria-label", token.title);
9312 // Build up the DOM representation for a single token, and add it to
9313 // the line map. Takes care to render special characters separately.
9314 function buildToken(builder, text, style, startStyle, endStyle, title, css) {
9316 var displayText = builder.splitSpaces ? text.replace(/ {3,}/g, splitSpaces) : text;
9317 var special = builder.cm.state.specialChars, mustWrap = false;
9318 if (!special.test(text)) {
9319 builder.col += text.length;
9320 var content = document.createTextNode(displayText);
9321 builder.map.push(builder.pos, builder.pos + text.length, content);
9322 if (ie && ie_version < 9) mustWrap = true;
9323 builder.pos += text.length;
9325 var content = document.createDocumentFragment(), pos = 0;
9327 special.lastIndex = pos;
9328 var m = special.exec(text);
9329 var skipped = m ? m.index - pos : text.length - pos;
9331 var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
9332 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
9333 else content.appendChild(txt);
9334 builder.map.push(builder.pos, builder.pos + skipped, txt);
9335 builder.col += skipped;
9336 builder.pos += skipped;
9341 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
9342 var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
9343 txt.setAttribute("role", "presentation");
9344 txt.setAttribute("cm-text", "\t");
9345 builder.col += tabWidth;
9346 } else if (m[0] == "\r" || m[0] == "\n") {
9347 var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
9348 txt.setAttribute("cm-text", m[0]);
9351 var txt = builder.cm.options.specialCharPlaceholder(m[0]);
9352 txt.setAttribute("cm-text", m[0]);
9353 if (ie && ie_version < 9) content.appendChild(elt("span", [txt]));
9354 else content.appendChild(txt);
9357 builder.map.push(builder.pos, builder.pos + 1, txt);
9361 if (style || startStyle || endStyle || mustWrap || css) {
9362 var fullStyle = style || "";
9363 if (startStyle) fullStyle += startStyle;
9364 if (endStyle) fullStyle += endStyle;
9365 var token = elt("span", [content], fullStyle, css);
9366 if (title) token.title = title;
9367 return builder.content.appendChild(token);
9369 builder.content.appendChild(content);
9372 function splitSpaces(old) {
9374 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
9379 // Work around nonsense dimensions being reported for stretches of
9380 // right-to-left text.
9381 function buildTokenBadBidi(inner, order) {
9382 return function(builder, text, style, startStyle, endStyle, title, css) {
9383 style = style ? style + " cm-force-border" : "cm-force-border";
9384 var start = builder.pos, end = start + text.length;
9386 // Find the part that overlaps with the start of this text
9387 for (var i = 0; i < order.length; i++) {
9388 var part = order[i];
9389 if (part.to > start && part.from <= start) break;
9391 if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css);
9392 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css);
9394 text = text.slice(part.to - start);
9400 function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
9401 var widget = !ignoreWidget && marker.widgetNode;
9402 if (widget) builder.map.push(builder.pos, builder.pos + size, widget);
9403 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
9405 widget = builder.content.appendChild(document.createElement("span"));
9406 widget.setAttribute("cm-marker", marker.id);
9409 builder.cm.display.input.setUneditable(widget);
9410 builder.content.appendChild(widget);
9412 builder.pos += size;
9415 // Outputs a number of spans to make up a line, taking highlighting
9416 // and marked text into account.
9417 function insertLineContent(line, builder, styles) {
9418 var spans = line.markedSpans, allText = line.text, at = 0;
9420 for (var i = 1; i < styles.length; i+=2)
9421 builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
9425 var len = allText.length, pos = 0, i = 1, text = "", style, css;
9426 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed;
9428 if (nextChange == pos) { // Update current marker set
9429 spanStyle = spanEndStyle = spanStartStyle = title = css = "";
9430 collapsed = null; nextChange = Infinity;
9431 var foundBookmarks = [], endStyles
9432 for (var j = 0; j < spans.length; ++j) {
9433 var sp = spans[j], m = sp.marker;
9434 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
9435 foundBookmarks.push(m);
9436 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
9437 if (sp.to != null && sp.to != pos && nextChange > sp.to) {
9441 if (m.className) spanStyle += " " + m.className;
9442 if (m.css) css = (css ? css + ";" : "") + m.css;
9443 if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
9444 if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to)
9445 if (m.title && !title) title = m.title;
9446 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
9448 } else if (sp.from > pos && nextChange > sp.from) {
9449 nextChange = sp.from;
9452 if (endStyles) for (var j = 0; j < endStyles.length; j += 2)
9453 if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j]
9455 if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j)
9456 buildCollapsedSpan(builder, 0, foundBookmarks[j]);
9457 if (collapsed && (collapsed.from || 0) == pos) {
9458 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
9459 collapsed.marker, collapsed.from == null);
9460 if (collapsed.to == null) return;
9461 if (collapsed.to == pos) collapsed = false;
9464 if (pos >= len) break;
9466 var upto = Math.min(len, nextChange);
9469 var end = pos + text.length;
9471 var tokenText = end > upto ? text.slice(0, upto - pos) : text;
9472 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
9473 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css);
9475 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
9477 spanStartStyle = "";
9479 text = allText.slice(at, at = styles[i++]);
9480 style = interpretTokenStyle(styles[i++], builder.cm.options);
9485 // DOCUMENT DATA STRUCTURE
9487 // By default, updates that start and end at the beginning of a line
9488 // are treated specially, in order to make the association of line
9489 // widgets and marker elements with the text behave more intuitive.
9490 function isWholeLineUpdate(doc, change) {
9491 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
9492 (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
9495 // Perform a change on the document data structure.
9496 function updateDoc(doc, change, markedSpans, estimateHeight) {
9497 function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
9498 function update(line, text, spans) {
9499 updateLine(line, text, spans, estimateHeight);
9500 signalLater(line, "change", line, change);
9502 function linesFor(start, end) {
9503 for (var i = start, result = []; i < end; ++i)
9504 result.push(new Line(text[i], spansFor(i), estimateHeight));
9508 var from = change.from, to = change.to, text = change.text;
9509 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
9510 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
9512 // Adjust the line structure
9514 doc.insert(0, linesFor(0, text.length));
9515 doc.remove(text.length, doc.size - text.length);
9516 } else if (isWholeLineUpdate(doc, change)) {
9517 // This is a whole-line replace. Treated specially to make
9518 // sure line objects move the way they are supposed to.
9519 var added = linesFor(0, text.length - 1);
9520 update(lastLine, lastLine.text, lastSpans);
9521 if (nlines) doc.remove(from.line, nlines);
9522 if (added.length) doc.insert(from.line, added);
9523 } else if (firstLine == lastLine) {
9524 if (text.length == 1) {
9525 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
9527 var added = linesFor(1, text.length - 1);
9528 added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
9529 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
9530 doc.insert(from.line + 1, added);
9532 } else if (text.length == 1) {
9533 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
9534 doc.remove(from.line + 1, nlines);
9536 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
9537 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
9538 var added = linesFor(1, text.length - 1);
9539 if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
9540 doc.insert(from.line + 1, added);
9543 signalLater(doc, "change", doc, change);
9546 // The document is represented as a BTree consisting of leaves, with
9547 // chunk of lines in them, and branches, with up to ten leaves or
9548 // other branch nodes below them. The top node is always a branch
9549 // node, and is the document object itself (meaning it has
9550 // additional methods and properties).
9552 // All nodes have parent links. The tree is used both to go from
9553 // line numbers to line objects, and to go from objects to numbers.
9554 // It also indexes by height, and is used to convert between height
9555 // and line object, and to find the total height of the document.
9557 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
9559 function LeafChunk(lines) {
9562 for (var i = 0, height = 0; i < lines.length; ++i) {
9563 lines[i].parent = this;
9564 height += lines[i].height;
9566 this.height = height;
9569 LeafChunk.prototype = {
9570 chunkSize: function() { return this.lines.length; },
9571 // Remove the n lines at offset 'at'.
9572 removeInner: function(at, n) {
9573 for (var i = at, e = at + n; i < e; ++i) {
9574 var line = this.lines[i];
9575 this.height -= line.height;
9577 signalLater(line, "delete");
9579 this.lines.splice(at, n);
9581 // Helper used to collapse a small branch into a single leaf.
9582 collapse: function(lines) {
9583 lines.push.apply(lines, this.lines);
9585 // Insert the given array of lines at offset 'at', count them as
9586 // having the given height.
9587 insertInner: function(at, lines, height) {
9588 this.height += height;
9589 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
9590 for (var i = 0; i < lines.length; ++i) lines[i].parent = this;
9592 // Used to iterate over a part of the tree.
9593 iterN: function(at, n, op) {
9594 for (var e = at + n; at < e; ++at)
9595 if (op(this.lines[at])) return true;
9599 function BranchChunk(children) {
9600 this.children = children;
9601 var size = 0, height = 0;
9602 for (var i = 0; i < children.length; ++i) {
9603 var ch = children[i];
9604 size += ch.chunkSize(); height += ch.height;
9608 this.height = height;
9612 BranchChunk.prototype = {
9613 chunkSize: function() { return this.size; },
9614 removeInner: function(at, n) {
9616 for (var i = 0; i < this.children.length; ++i) {
9617 var child = this.children[i], sz = child.chunkSize();
9619 var rm = Math.min(n, sz - at), oldHeight = child.height;
9620 child.removeInner(at, rm);
9621 this.height -= oldHeight - child.height;
9622 if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
9623 if ((n -= rm) == 0) break;
9627 // If the result is smaller than 25 lines, ensure that it is a
9628 // single leaf node.
9629 if (this.size - n < 25 &&
9630 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
9632 this.collapse(lines);
9633 this.children = [new LeafChunk(lines)];
9634 this.children[0].parent = this;
9637 collapse: function(lines) {
9638 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines);
9640 insertInner: function(at, lines, height) {
9641 this.size += lines.length;
9642 this.height += height;
9643 for (var i = 0; i < this.children.length; ++i) {
9644 var child = this.children[i], sz = child.chunkSize();
9646 child.insertInner(at, lines, height);
9647 if (child.lines && child.lines.length > 50) {
9648 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
9649 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
9650 var remaining = child.lines.length % 25 + 25
9651 for (var pos = remaining; pos < child.lines.length;) {
9652 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
9653 child.height -= leaf.height;
9654 this.children.splice(++i, 0, leaf);
9657 child.lines = child.lines.slice(0, remaining);
9665 // When a node has grown, check whether it should be split.
9666 maybeSpill: function() {
9667 if (this.children.length <= 10) return;
9670 var spilled = me.children.splice(me.children.length - 5, 5);
9671 var sibling = new BranchChunk(spilled);
9672 if (!me.parent) { // Become the parent node
9673 var copy = new BranchChunk(me.children);
9675 me.children = [copy, sibling];
9678 me.size -= sibling.size;
9679 me.height -= sibling.height;
9680 var myIndex = indexOf(me.parent.children, me);
9681 me.parent.children.splice(myIndex + 1, 0, sibling);
9683 sibling.parent = me.parent;
9684 } while (me.children.length > 10);
9685 me.parent.maybeSpill();
9687 iterN: function(at, n, op) {
9688 for (var i = 0; i < this.children.length; ++i) {
9689 var child = this.children[i], sz = child.chunkSize();
9691 var used = Math.min(n, sz - at);
9692 if (child.iterN(at, used, op)) return true;
9693 if ((n -= used) == 0) break;
9701 var Doc = CodeMirror.Doc = function(text, mode, firstLine, lineSep) {
9702 if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep);
9703 if (firstLine == null) firstLine = 0;
9705 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
9706 this.first = firstLine;
9707 this.scrollTop = this.scrollLeft = 0;
9708 this.cantEdit = false;
9709 this.cleanGeneration = 1;
9710 this.frontier = firstLine;
9711 var start = Pos(firstLine, 0);
9712 this.sel = simpleSelection(start);
9713 this.history = new History(null);
9714 this.id = ++nextDocId;
9715 this.modeOption = mode;
9716 this.lineSep = lineSep;
9717 this.extend = false;
9719 if (typeof text == "string") text = this.splitLines(text);
9720 updateDoc(this, {from: start, to: start, text: text});
9721 setSelection(this, simpleSelection(start), sel_dontScroll);
9724 Doc.prototype = createObj(BranchChunk.prototype, {
9726 // Iterate over the document. Supports two forms -- with only one
9727 // argument, it calls that for each line in the document. With
9728 // three, it iterates over the range given by the first two (with
9729 // the second being non-inclusive).
9730 iter: function(from, to, op) {
9731 if (op) this.iterN(from - this.first, to - from, op);
9732 else this.iterN(this.first, this.first + this.size, from);
9735 // Non-public interface for adding and removing lines.
9736 insert: function(at, lines) {
9738 for (var i = 0; i < lines.length; ++i) height += lines[i].height;
9739 this.insertInner(at - this.first, lines, height);
9741 remove: function(at, n) { this.removeInner(at - this.first, n); },
9743 // From here, the methods are part of the public interface. Most
9744 // are also available from CodeMirror (editor) instances.
9746 getValue: function(lineSep) {
9747 var lines = getLines(this, this.first, this.first + this.size);
9748 if (lineSep === false) return lines;
9749 return lines.join(lineSep || this.lineSeparator());
9751 setValue: docMethodOp(function(code) {
9752 var top = Pos(this.first, 0), last = this.first + this.size - 1;
9753 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
9754 text: this.splitLines(code), origin: "setValue", full: true}, true);
9755 setSelection(this, simpleSelection(top));
9757 replaceRange: function(code, from, to, origin) {
9758 from = clipPos(this, from);
9759 to = to ? clipPos(this, to) : from;
9760 replaceRange(this, code, from, to, origin);
9762 getRange: function(from, to, lineSep) {
9763 var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
9764 if (lineSep === false) return lines;
9765 return lines.join(lineSep || this.lineSeparator());
9768 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
9770 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
9771 getLineNumber: function(line) {return lineNo(line);},
9773 getLineHandleVisualStart: function(line) {
9774 if (typeof line == "number") line = getLine(this, line);
9775 return visualLine(line);
9778 lineCount: function() {return this.size;},
9779 firstLine: function() {return this.first;},
9780 lastLine: function() {return this.first + this.size - 1;},
9782 clipPos: function(pos) {return clipPos(this, pos);},
9784 getCursor: function(start) {
9785 var range = this.sel.primary(), pos;
9786 if (start == null || start == "head") pos = range.head;
9787 else if (start == "anchor") pos = range.anchor;
9788 else if (start == "end" || start == "to" || start === false) pos = range.to();
9789 else pos = range.from();
9792 listSelections: function() { return this.sel.ranges; },
9793 somethingSelected: function() {return this.sel.somethingSelected();},
9795 setCursor: docMethodOp(function(line, ch, options) {
9796 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
9798 setSelection: docMethodOp(function(anchor, head, options) {
9799 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
9801 extendSelection: docMethodOp(function(head, other, options) {
9802 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
9804 extendSelections: docMethodOp(function(heads, options) {
9805 extendSelections(this, clipPosArray(this, heads), options);
9807 extendSelectionsBy: docMethodOp(function(f, options) {
9808 var heads = map(this.sel.ranges, f);
9809 extendSelections(this, clipPosArray(this, heads), options);
9811 setSelections: docMethodOp(function(ranges, primary, options) {
9812 if (!ranges.length) return;
9813 for (var i = 0, out = []; i < ranges.length; i++)
9814 out[i] = new Range(clipPos(this, ranges[i].anchor),
9815 clipPos(this, ranges[i].head));
9816 if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
9817 setSelection(this, normalizeSelection(out, primary), options);
9819 addSelection: docMethodOp(function(anchor, head, options) {
9820 var ranges = this.sel.ranges.slice(0);
9821 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
9822 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
9825 getSelection: function(lineSep) {
9826 var ranges = this.sel.ranges, lines;
9827 for (var i = 0; i < ranges.length; i++) {
9828 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
9829 lines = lines ? lines.concat(sel) : sel;
9831 if (lineSep === false) return lines;
9832 else return lines.join(lineSep || this.lineSeparator());
9834 getSelections: function(lineSep) {
9835 var parts = [], ranges = this.sel.ranges;
9836 for (var i = 0; i < ranges.length; i++) {
9837 var sel = getBetween(this, ranges[i].from(), ranges[i].to());
9838 if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator());
9843 replaceSelection: function(code, collapse, origin) {
9845 for (var i = 0; i < this.sel.ranges.length; i++)
9847 this.replaceSelections(dup, collapse, origin || "+input");
9849 replaceSelections: docMethodOp(function(code, collapse, origin) {
9850 var changes = [], sel = this.sel;
9851 for (var i = 0; i < sel.ranges.length; i++) {
9852 var range = sel.ranges[i];
9853 changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
9855 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
9856 for (var i = changes.length - 1; i >= 0; i--)
9857 makeChange(this, changes[i]);
9858 if (newSel) setSelectionReplaceHistory(this, newSel);
9859 else if (this.cm) ensureCursorVisible(this.cm);
9861 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
9862 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
9863 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
9864 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
9866 setExtending: function(val) {this.extend = val;},
9867 getExtending: function() {return this.extend;},
9869 historySize: function() {
9870 var hist = this.history, done = 0, undone = 0;
9871 for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done;
9872 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
9873 return {undo: done, redo: undone};
9875 clearHistory: function() {this.history = new History(this.history.maxGeneration);},
9877 markClean: function() {
9878 this.cleanGeneration = this.changeGeneration(true);
9880 changeGeneration: function(forceSplit) {
9882 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
9883 return this.history.generation;
9885 isClean: function (gen) {
9886 return this.history.generation == (gen || this.cleanGeneration);
9889 getHistory: function() {
9890 return {done: copyHistoryArray(this.history.done),
9891 undone: copyHistoryArray(this.history.undone)};
9893 setHistory: function(histData) {
9894 var hist = this.history = new History(this.history.maxGeneration);
9895 hist.done = copyHistoryArray(histData.done.slice(0), null, true);
9896 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
9899 addLineClass: docMethodOp(function(handle, where, cls) {
9900 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
9901 var prop = where == "text" ? "textClass"
9902 : where == "background" ? "bgClass"
9903 : where == "gutter" ? "gutterClass" : "wrapClass";
9904 if (!line[prop]) line[prop] = cls;
9905 else if (classTest(cls).test(line[prop])) return false;
9906 else line[prop] += " " + cls;
9910 removeLineClass: docMethodOp(function(handle, where, cls) {
9911 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
9912 var prop = where == "text" ? "textClass"
9913 : where == "background" ? "bgClass"
9914 : where == "gutter" ? "gutterClass" : "wrapClass";
9915 var cur = line[prop];
9916 if (!cur) return false;
9917 else if (cls == null) line[prop] = null;
9919 var found = cur.match(classTest(cls));
9920 if (!found) return false;
9921 var end = found.index + found[0].length;
9922 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
9928 addLineWidget: docMethodOp(function(handle, node, options) {
9929 return addLineWidget(this, handle, node, options);
9931 removeLineWidget: function(widget) { widget.clear(); },
9933 markText: function(from, to, options) {
9934 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range");
9936 setBookmark: function(pos, options) {
9937 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
9938 insertLeft: options && options.insertLeft,
9939 clearWhenEmpty: false, shared: options && options.shared,
9940 handleMouseEvents: options && options.handleMouseEvents};
9941 pos = clipPos(this, pos);
9942 return markText(this, pos, pos, realOpts, "bookmark");
9944 findMarksAt: function(pos) {
9945 pos = clipPos(this, pos);
9946 var markers = [], spans = getLine(this, pos.line).markedSpans;
9947 if (spans) for (var i = 0; i < spans.length; ++i) {
9948 var span = spans[i];
9949 if ((span.from == null || span.from <= pos.ch) &&
9950 (span.to == null || span.to >= pos.ch))
9951 markers.push(span.marker.parent || span.marker);
9955 findMarks: function(from, to, filter) {
9956 from = clipPos(this, from); to = clipPos(this, to);
9957 var found = [], lineNo = from.line;
9958 this.iter(from.line, to.line + 1, function(line) {
9959 var spans = line.markedSpans;
9960 if (spans) for (var i = 0; i < spans.length; i++) {
9961 var span = spans[i];
9962 if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
9963 span.from == null && lineNo != from.line ||
9964 span.from != null && lineNo == to.line && span.from >= to.ch) &&
9965 (!filter || filter(span.marker)))
9966 found.push(span.marker.parent || span.marker);
9972 getAllMarks: function() {
9974 this.iter(function(line) {
9975 var sps = line.markedSpans;
9976 if (sps) for (var i = 0; i < sps.length; ++i)
9977 if (sps[i].from != null) markers.push(sps[i].marker);
9982 posFromIndex: function(off) {
9983 var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
9984 this.iter(function(line) {
9985 var sz = line.text.length + sepSize;
9986 if (sz > off) { ch = off; return true; }
9990 return clipPos(this, Pos(lineNo, ch));
9992 indexFromPos: function (coords) {
9993 coords = clipPos(this, coords);
9994 var index = coords.ch;
9995 if (coords.line < this.first || coords.ch < 0) return 0;
9996 var sepSize = this.lineSeparator().length;
9997 this.iter(this.first, coords.line, function (line) {
9998 index += line.text.length + sepSize;
10003 copy: function(copyHistory) {
10004 var doc = new Doc(getLines(this, this.first, this.first + this.size),
10005 this.modeOption, this.first, this.lineSep);
10006 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
10007 doc.sel = this.sel;
10008 doc.extend = false;
10010 doc.history.undoDepth = this.history.undoDepth;
10011 doc.setHistory(this.getHistory());
10016 linkedDoc: function(options) {
10017 if (!options) options = {};
10018 var from = this.first, to = this.first + this.size;
10019 if (options.from != null && options.from > from) from = options.from;
10020 if (options.to != null && options.to < to) to = options.to;
10021 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep);
10022 if (options.sharedHist) copy.history = this.history;
10023 (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
10024 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
10025 copySharedMarkers(copy, findSharedMarkers(this));
10028 unlinkDoc: function(other) {
10029 if (other instanceof CodeMirror) other = other.doc;
10030 if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
10031 var link = this.linked[i];
10032 if (link.doc != other) continue;
10033 this.linked.splice(i, 1);
10034 other.unlinkDoc(this);
10035 detachSharedMarkers(findSharedMarkers(this));
10038 // If the histories were shared, split them again
10039 if (other.history == this.history) {
10040 var splitIds = [other.id];
10041 linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
10042 other.history = new History(null);
10043 other.history.done = copyHistoryArray(this.history.done, splitIds);
10044 other.history.undone = copyHistoryArray(this.history.undone, splitIds);
10047 iterLinkedDocs: function(f) {linkedDocs(this, f);},
10049 getMode: function() {return this.mode;},
10050 getEditor: function() {return this.cm;},
10052 splitLines: function(str) {
10053 if (this.lineSep) return str.split(this.lineSep);
10054 return splitLinesAuto(str);
10056 lineSeparator: function() { return this.lineSep || "\n"; }
10060 Doc.prototype.eachLine = Doc.prototype.iter;
10062 // Set up methods on CodeMirror's prototype to redirect to the editor's document.
10063 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
10064 for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
10065 CodeMirror.prototype[prop] = (function(method) {
10066 return function() {return method.apply(this.doc, arguments);};
10067 })(Doc.prototype[prop]);
10071 // Call f for all linked documents.
10072 function linkedDocs(doc, f, sharedHistOnly) {
10073 function propagate(doc, skip, sharedHist) {
10074 if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
10075 var rel = doc.linked[i];
10076 if (rel.doc == skip) continue;
10077 var shared = sharedHist && rel.sharedHist;
10078 if (sharedHistOnly && !shared) continue;
10079 f(rel.doc, shared);
10080 propagate(rel.doc, doc, shared);
10083 propagate(doc, null, true);
10086 // Attach a document to an editor.
10087 function attachDoc(cm, doc) {
10088 if (doc.cm) throw new Error("This document is already in use.");
10091 estimateLineHeights(cm);
10093 if (!cm.options.lineWrapping) findMaxLine(cm);
10094 cm.options.mode = doc.modeOption;
10100 // Find the line object corresponding to the given line number.
10101 function getLine(doc, n) {
10103 if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.");
10104 for (var chunk = doc; !chunk.lines;) {
10105 for (var i = 0;; ++i) {
10106 var child = chunk.children[i], sz = child.chunkSize();
10107 if (n < sz) { chunk = child; break; }
10111 return chunk.lines[n];
10114 // Get the part of a document between two positions, as an array of
10116 function getBetween(doc, start, end) {
10117 var out = [], n = start.line;
10118 doc.iter(start.line, end.line + 1, function(line) {
10119 var text = line.text;
10120 if (n == end.line) text = text.slice(0, end.ch);
10121 if (n == start.line) text = text.slice(start.ch);
10127 // Get the lines between from and to, as array of strings.
10128 function getLines(doc, from, to) {
10130 doc.iter(from, to, function(line) { out.push(line.text); });
10134 // Update the height of a line, propagating the height change
10135 // upwards to parent nodes.
10136 function updateLineHeight(line, height) {
10137 var diff = height - line.height;
10138 if (diff) for (var n = line; n; n = n.parent) n.height += diff;
10141 // Given a line object, find its line number by walking up through
10142 // its parent links.
10143 function lineNo(line) {
10144 if (line.parent == null) return null;
10145 var cur = line.parent, no = indexOf(cur.lines, line);
10146 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
10147 for (var i = 0;; ++i) {
10148 if (chunk.children[i] == cur) break;
10149 no += chunk.children[i].chunkSize();
10152 return no + cur.first;
10155 // Find the line at the given vertical position, using the height
10156 // information in the document tree.
10157 function lineAtHeight(chunk, h) {
10158 var n = chunk.first;
10160 for (var i = 0; i < chunk.children.length; ++i) {
10161 var child = chunk.children[i], ch = child.height;
10162 if (h < ch) { chunk = child; continue outer; }
10164 n += child.chunkSize();
10167 } while (!chunk.lines);
10168 for (var i = 0; i < chunk.lines.length; ++i) {
10169 var line = chunk.lines[i], lh = line.height;
10177 // Find the height above the given line.
10178 function heightAtLine(lineObj) {
10179 lineObj = visualLine(lineObj);
10181 var h = 0, chunk = lineObj.parent;
10182 for (var i = 0; i < chunk.lines.length; ++i) {
10183 var line = chunk.lines[i];
10184 if (line == lineObj) break;
10185 else h += line.height;
10187 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
10188 for (var i = 0; i < p.children.length; ++i) {
10189 var cur = p.children[i];
10190 if (cur == chunk) break;
10191 else h += cur.height;
10197 // Get the bidi ordering for the given line (and cache it). Returns
10198 // false for lines that are fully left-to-right, and an array of
10199 // BidiSpan objects otherwise.
10200 function getOrder(line) {
10201 var order = line.order;
10202 if (order == null) order = line.order = bidiOrdering(line.text);
10208 function History(startGen) {
10209 // Arrays of change events and selections. Doing something adds an
10210 // event to done and clears undo. Undoing moves events from done
10211 // to undone, redoing moves them in the other direction.
10212 this.done = []; this.undone = [];
10213 this.undoDepth = Infinity;
10214 // Used to track when changes can be merged into a single undo
10216 this.lastModTime = this.lastSelTime = 0;
10217 this.lastOp = this.lastSelOp = null;
10218 this.lastOrigin = this.lastSelOrigin = null;
10219 // Used by the isClean() method
10220 this.generation = this.maxGeneration = startGen || 1;
10223 // Create a history change event from an updateDoc-style change
10225 function historyChangeFromChange(doc, change) {
10226 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
10227 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
10228 linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
10232 // Pop all selection events off the end of a history array. Stop at
10234 function clearSelectionEvents(array) {
10235 while (array.length) {
10236 var last = lst(array);
10237 if (last.ranges) array.pop();
10242 // Find the top change event in the history. Pop off selection
10243 // events that are in the way.
10244 function lastChangeEvent(hist, force) {
10246 clearSelectionEvents(hist.done);
10247 return lst(hist.done);
10248 } else if (hist.done.length && !lst(hist.done).ranges) {
10249 return lst(hist.done);
10250 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
10252 return lst(hist.done);
10256 // Register a change in the history. Merges changes that are within
10257 // a single operation, ore are close together with an origin that
10258 // allows merging (starting with "+") into a single event.
10259 function addChangeToHistory(doc, change, selAfter, opId) {
10260 var hist = doc.history;
10261 hist.undone.length = 0;
10262 var time = +new Date, cur;
10264 if ((hist.lastOp == opId ||
10265 hist.lastOrigin == change.origin && change.origin &&
10266 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) ||
10267 change.origin.charAt(0) == "*")) &&
10268 (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
10269 // Merge this change into the last event
10270 var last = lst(cur.changes);
10271 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
10272 // Optimized case for simple insertion -- don't want to add
10273 // new changesets for every character typed
10274 last.to = changeEnd(change);
10276 // Add new sub-event
10277 cur.changes.push(historyChangeFromChange(doc, change));
10280 // Can not be merged, start a new event.
10281 var before = lst(hist.done);
10282 if (!before || !before.ranges)
10283 pushSelectionToHistory(doc.sel, hist.done);
10284 cur = {changes: [historyChangeFromChange(doc, change)],
10285 generation: hist.generation};
10286 hist.done.push(cur);
10287 while (hist.done.length > hist.undoDepth) {
10289 if (!hist.done[0].ranges) hist.done.shift();
10292 hist.done.push(selAfter);
10293 hist.generation = ++hist.maxGeneration;
10294 hist.lastModTime = hist.lastSelTime = time;
10295 hist.lastOp = hist.lastSelOp = opId;
10296 hist.lastOrigin = hist.lastSelOrigin = change.origin;
10298 if (!last) signal(doc, "historyAdded");
10301 function selectionEventCanBeMerged(doc, origin, prev, sel) {
10302 var ch = origin.charAt(0);
10303 return ch == "*" ||
10305 prev.ranges.length == sel.ranges.length &&
10306 prev.somethingSelected() == sel.somethingSelected() &&
10307 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500);
10310 // Called whenever the selection changes, sets the new selection as
10311 // the pending selection in the history, and pushes the old pending
10312 // selection into the 'done' array when it was significantly
10313 // different (in number of selected ranges, emptiness, or time).
10314 function addSelectionToHistory(doc, sel, opId, options) {
10315 var hist = doc.history, origin = options && options.origin;
10317 // A new event is started when the previous origin does not match
10318 // the current, or the origins don't allow matching. Origins
10319 // starting with * are always merged, those starting with + are
10320 // merged when similar and close together in time.
10321 if (opId == hist.lastSelOp ||
10322 (origin && hist.lastSelOrigin == origin &&
10323 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
10324 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
10325 hist.done[hist.done.length - 1] = sel;
10327 pushSelectionToHistory(sel, hist.done);
10329 hist.lastSelTime = +new Date;
10330 hist.lastSelOrigin = origin;
10331 hist.lastSelOp = opId;
10332 if (options && options.clearRedo !== false)
10333 clearSelectionEvents(hist.undone);
10336 function pushSelectionToHistory(sel, dest) {
10337 var top = lst(dest);
10338 if (!(top && top.ranges && top.equals(sel)))
10342 // Used to store marked span information in the history.
10343 function attachLocalSpans(doc, change, from, to) {
10344 var existing = change["spans_" + doc.id], n = 0;
10345 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
10346 if (line.markedSpans)
10347 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
10352 // When un/re-doing restores text containing marked spans, those
10353 // that have been explicitly cleared should not be restored.
10354 function removeClearedSpans(spans) {
10355 if (!spans) return null;
10356 for (var i = 0, out; i < spans.length; ++i) {
10357 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
10358 else if (out) out.push(spans[i]);
10360 return !out ? spans : out.length ? out : null;
10363 // Retrieve and filter the old marked spans stored in a change event.
10364 function getOldSpans(doc, change) {
10365 var found = change["spans_" + doc.id];
10366 if (!found) return null;
10367 for (var i = 0, nw = []; i < change.text.length; ++i)
10368 nw.push(removeClearedSpans(found[i]));
10372 // Used both to provide a JSON-safe object in .getHistory, and, when
10373 // detaching a document, to split the history in two
10374 function copyHistoryArray(events, newGroup, instantiateSel) {
10375 for (var i = 0, copy = []; i < events.length; ++i) {
10376 var event = events[i];
10377 if (event.ranges) {
10378 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
10381 var changes = event.changes, newChanges = [];
10382 copy.push({changes: newChanges});
10383 for (var j = 0; j < changes.length; ++j) {
10384 var change = changes[j], m;
10385 newChanges.push({from: change.from, to: change.to, text: change.text});
10386 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
10387 if (indexOf(newGroup, Number(m[1])) > -1) {
10388 lst(newChanges)[prop] = change[prop];
10389 delete change[prop];
10397 // Rebasing/resetting history to deal with externally-sourced changes
10399 function rebaseHistSelSingle(pos, from, to, diff) {
10400 if (to < pos.line) {
10402 } else if (from < pos.line) {
10408 // Tries to rebase an array of history events given a change in the
10409 // document. If the change touches the same lines as the event, the
10410 // event, and everything 'behind' it, is discarded. If the change is
10411 // before the event, the event's positions are updated. Uses a
10412 // copy-on-write scheme for the positions, to avoid having to
10413 // reallocate them all on every rebase, but also avoid problems with
10414 // shared position objects being unsafely updated.
10415 function rebaseHistArray(array, from, to, diff) {
10416 for (var i = 0; i < array.length; ++i) {
10417 var sub = array[i], ok = true;
10419 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
10420 for (var j = 0; j < sub.ranges.length; j++) {
10421 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
10422 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
10426 for (var j = 0; j < sub.changes.length; ++j) {
10427 var cur = sub.changes[j];
10428 if (to < cur.from.line) {
10429 cur.from = Pos(cur.from.line + diff, cur.from.ch);
10430 cur.to = Pos(cur.to.line + diff, cur.to.ch);
10431 } else if (from <= cur.to.line) {
10437 array.splice(0, i + 1);
10443 function rebaseHist(hist, change) {
10444 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
10445 rebaseHistArray(hist.done, from, to, diff);
10446 rebaseHistArray(hist.undone, from, to, diff);
10451 // Due to the fact that we still support jurassic IE versions, some
10452 // compatibility wrappers are needed.
10454 var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
10455 if (e.preventDefault) e.preventDefault();
10456 else e.returnValue = false;
10458 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
10459 if (e.stopPropagation) e.stopPropagation();
10460 else e.cancelBubble = true;
10462 function e_defaultPrevented(e) {
10463 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
10465 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
10467 function e_target(e) {return e.target || e.srcElement;}
10468 function e_button(e) {
10471 if (e.button & 1) b = 1;
10472 else if (e.button & 2) b = 3;
10473 else if (e.button & 4) b = 2;
10475 if (mac && e.ctrlKey && b == 1) b = 3;
10481 // Lightweight event framework. on/off also work on DOM nodes,
10482 // registering native DOM handlers.
10484 var on = CodeMirror.on = function(emitter, type, f) {
10485 if (emitter.addEventListener)
10486 emitter.addEventListener(type, f, false);
10487 else if (emitter.attachEvent)
10488 emitter.attachEvent("on" + type, f);
10490 var map = emitter._handlers || (emitter._handlers = {});
10491 var arr = map[type] || (map[type] = []);
10496 var noHandlers = []
10497 function getHandlers(emitter, type, copy) {
10498 var arr = emitter._handlers && emitter._handlers[type]
10499 if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers
10500 else return arr || noHandlers
10503 var off = CodeMirror.off = function(emitter, type, f) {
10504 if (emitter.removeEventListener)
10505 emitter.removeEventListener(type, f, false);
10506 else if (emitter.detachEvent)
10507 emitter.detachEvent("on" + type, f);
10509 var handlers = getHandlers(emitter, type, false)
10510 for (var i = 0; i < handlers.length; ++i)
10511 if (handlers[i] == f) { handlers.splice(i, 1); break; }
10515 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
10516 var handlers = getHandlers(emitter, type, true)
10517 if (!handlers.length) return;
10518 var args = Array.prototype.slice.call(arguments, 2);
10519 for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args);
10522 var orphanDelayedCallbacks = null;
10524 // Often, we want to signal events at a point where we are in the
10525 // middle of some work, but don't want the handler to start calling
10526 // other methods on the editor, which might be in an inconsistent
10527 // state or simply not expect any other events to happen.
10528 // signalLater looks whether there are any handlers, and schedules
10529 // them to be executed when the last operation ends, or, if no
10530 // operation is active, when a timeout fires.
10531 function signalLater(emitter, type /*, values...*/) {
10532 var arr = getHandlers(emitter, type, false)
10533 if (!arr.length) return;
10534 var args = Array.prototype.slice.call(arguments, 2), list;
10535 if (operationGroup) {
10536 list = operationGroup.delayedCallbacks;
10537 } else if (orphanDelayedCallbacks) {
10538 list = orphanDelayedCallbacks;
10540 list = orphanDelayedCallbacks = [];
10541 setTimeout(fireOrphanDelayed, 0);
10543 function bnd(f) {return function(){f.apply(null, args);};};
10544 for (var i = 0; i < arr.length; ++i)
10545 list.push(bnd(arr[i]));
10548 function fireOrphanDelayed() {
10549 var delayed = orphanDelayedCallbacks;
10550 orphanDelayedCallbacks = null;
10551 for (var i = 0; i < delayed.length; ++i) delayed[i]();
10554 // The DOM events that CodeMirror handles can be overridden by
10555 // registering a (non-DOM) handler on the editor for the event name,
10556 // and preventDefault-ing the event in that handler.
10557 function signalDOMEvent(cm, e, override) {
10558 if (typeof e == "string")
10559 e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
10560 signal(cm, override || e.type, cm, e);
10561 return e_defaultPrevented(e) || e.codemirrorIgnore;
10564 function signalCursorActivity(cm) {
10565 var arr = cm._handlers && cm._handlers.cursorActivity;
10567 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
10568 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1)
10572 function hasHandler(emitter, type) {
10573 return getHandlers(emitter, type).length > 0
10576 // Add on and off methods to a constructor's prototype, to make
10577 // registering events on such objects more convenient.
10578 function eventMixin(ctor) {
10579 ctor.prototype.on = function(type, f) {on(this, type, f);};
10580 ctor.prototype.off = function(type, f) {off(this, type, f);};
10585 // Number of pixels added to scroller and sizer to hide scrollbar
10586 var scrollerGap = 30;
10588 // Returned or thrown by various protocols to signal 'I'm not
10590 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
10592 // Reused option objects for setSelection & friends
10593 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
10595 function Delayed() {this.id = null;}
10596 Delayed.prototype.set = function(ms, f) {
10597 clearTimeout(this.id);
10598 this.id = setTimeout(f, ms);
10601 // Counts the column offset in a string, taking tabs into account.
10602 // Used mostly to find indentation.
10603 var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
10605 end = string.search(/[^\s\u00a0]/);
10606 if (end == -1) end = string.length;
10608 for (var i = startIndex || 0, n = startValue || 0;;) {
10609 var nextTab = string.indexOf("\t", i);
10610 if (nextTab < 0 || nextTab >= end)
10611 return n + (end - i);
10613 n += tabSize - (n % tabSize);
10618 // The inverse of countColumn -- find the offset that corresponds to
10619 // a particular column.
10620 var findColumn = CodeMirror.findColumn = function(string, goal, tabSize) {
10621 for (var pos = 0, col = 0;;) {
10622 var nextTab = string.indexOf("\t", pos);
10623 if (nextTab == -1) nextTab = string.length;
10624 var skipped = nextTab - pos;
10625 if (nextTab == string.length || col + skipped >= goal)
10626 return pos + Math.min(skipped, goal - col);
10627 col += nextTab - pos;
10628 col += tabSize - (col % tabSize);
10630 if (col >= goal) return pos;
10634 var spaceStrs = [""];
10635 function spaceStr(n) {
10636 while (spaceStrs.length <= n)
10637 spaceStrs.push(lst(spaceStrs) + " ");
10638 return spaceStrs[n];
10641 function lst(arr) { return arr[arr.length-1]; }
10643 var selectInput = function(node) { node.select(); };
10644 if (ios) // Mobile Safari apparently has a bug where select() is broken.
10645 selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
10646 else if (ie) // Suppress mysterious IE10 errors
10647 selectInput = function(node) { try { node.select(); } catch(_e) {} };
10649 function indexOf(array, elt) {
10650 for (var i = 0; i < array.length; ++i)
10651 if (array[i] == elt) return i;
10654 function map(array, f) {
10656 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i);
10660 function nothing() {}
10662 function createObj(base, props) {
10664 if (Object.create) {
10665 inst = Object.create(base);
10667 nothing.prototype = base;
10668 inst = new nothing();
10670 if (props) copyObj(props, inst);
10674 function copyObj(obj, target, overwrite) {
10675 if (!target) target = {};
10676 for (var prop in obj)
10677 if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
10678 target[prop] = obj[prop];
10683 var args = Array.prototype.slice.call(arguments, 1);
10684 return function(){return f.apply(null, args);};
10687 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
10688 var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
10689 return /\w/.test(ch) || ch > "\x80" &&
10690 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
10692 function isWordChar(ch, helper) {
10693 if (!helper) return isWordCharBasic(ch);
10694 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true;
10695 return helper.test(ch);
10698 function isEmpty(obj) {
10699 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
10703 // Extending unicode characters. A series of a non-extending char +
10704 // any number of extending chars is treated as a single unit as far
10705 // as editing and measuring is concerned. This is not fully correct,
10706 // since some scripts/fonts/browsers also treat other configurations
10707 // of code points as a group.
10708 var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
10709 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); }
10713 function elt(tag, content, className, style) {
10714 var e = document.createElement(tag);
10715 if (className) e.className = className;
10716 if (style) e.style.cssText = style;
10717 if (typeof content == "string") e.appendChild(document.createTextNode(content));
10718 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
10723 if (document.createRange) range = function(node, start, end, endNode) {
10724 var r = document.createRange();
10725 r.setEnd(endNode || node, end);
10726 r.setStart(node, start);
10729 else range = function(node, start, end) {
10730 var r = document.body.createTextRange();
10731 try { r.moveToElementText(node.parentNode); }
10732 catch(e) { return r; }
10734 r.moveEnd("character", end);
10735 r.moveStart("character", start);
10739 function removeChildren(e) {
10740 for (var count = e.childNodes.length; count > 0; --count)
10741 e.removeChild(e.firstChild);
10745 function removeChildrenAndAdd(parent, e) {
10746 return removeChildren(parent).appendChild(e);
10749 var contains = CodeMirror.contains = function(parent, child) {
10750 if (child.nodeType == 3) // Android browser always returns false when child is a textnode
10751 child = child.parentNode;
10752 if (parent.contains)
10753 return parent.contains(child);
10755 if (child.nodeType == 11) child = child.host;
10756 if (child == parent) return true;
10757 } while (child = child.parentNode);
10760 function activeElt() {
10761 var activeElement = document.activeElement;
10762 while (activeElement && activeElement.root && activeElement.root.activeElement)
10763 activeElement = activeElement.root.activeElement;
10764 return activeElement;
10766 // Older versions of IE throws unspecified error when touching
10767 // document.activeElement in some cases (during loading, in iframe)
10768 if (ie && ie_version < 11) activeElt = function() {
10769 try { return document.activeElement; }
10770 catch(e) { return document.body; }
10773 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
10774 var rmClass = CodeMirror.rmClass = function(node, cls) {
10775 var current = node.className;
10776 var match = classTest(cls).exec(current);
10778 var after = current.slice(match.index + match[0].length);
10779 node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
10782 var addClass = CodeMirror.addClass = function(node, cls) {
10783 var current = node.className;
10784 if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
10786 function joinClasses(a, b) {
10787 var as = a.split(" ");
10788 for (var i = 0; i < as.length; i++)
10789 if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i];
10793 // WINDOW-WIDE EVENTS
10795 // These must be handled carefully, because naively registering a
10796 // handler for each editor will cause the editors to never be
10797 // garbage collected.
10799 function forEachCodeMirror(f) {
10800 if (!document.body.getElementsByClassName) return;
10801 var byClass = document.body.getElementsByClassName("CodeMirror");
10802 for (var i = 0; i < byClass.length; i++) {
10803 var cm = byClass[i].CodeMirror;
10808 var globalsRegistered = false;
10809 function ensureGlobalHandlers() {
10810 if (globalsRegistered) return;
10811 registerGlobalHandlers();
10812 globalsRegistered = true;
10814 function registerGlobalHandlers() {
10815 // When the window resizes, we need to refresh active editors.
10817 on(window, "resize", function() {
10818 if (resizeTimer == null) resizeTimer = setTimeout(function() {
10819 resizeTimer = null;
10820 forEachCodeMirror(onResize);
10823 // When the window loses focus, we want to show the editor as blurred
10824 on(window, "blur", function() {
10825 forEachCodeMirror(onBlur);
10829 // FEATURE DETECTION
10831 // Detect drag-and-drop
10832 var dragAndDrop = function() {
10833 // There is *some* kind of drag-and-drop support in IE6-8, but I
10834 // couldn't get it to work yet.
10835 if (ie && ie_version < 9) return false;
10836 var div = elt('div');
10837 return "draggable" in div || "dragDrop" in div;
10841 function zeroWidthElement(measure) {
10842 if (zwspSupported == null) {
10843 var test = elt("span", "\u200b");
10844 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
10845 if (measure.firstChild.offsetHeight != 0)
10846 zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8);
10848 var node = zwspSupported ? elt("span", "\u200b") :
10849 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
10850 node.setAttribute("cm-text", "");
10854 // Feature-detect IE's crummy client rect reporting for bidi text
10856 function hasBadBidiRects(measure) {
10857 if (badBidiRects != null) return badBidiRects;
10858 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
10859 var r0 = range(txt, 0, 1).getBoundingClientRect();
10860 if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
10861 var r1 = range(txt, 1, 2).getBoundingClientRect();
10862 return badBidiRects = (r1.right - r0.right < 3);
10865 // See if "".split is the broken IE version, if so, provide an
10866 // alternative way to split lines.
10867 var splitLinesAuto = CodeMirror.splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
10868 var pos = 0, result = [], l = string.length;
10870 var nl = string.indexOf("\n", pos);
10871 if (nl == -1) nl = string.length;
10872 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
10873 var rt = line.indexOf("\r");
10875 result.push(line.slice(0, rt));
10883 } : function(string){return string.split(/\r\n?|\n/);};
10885 var hasSelection = window.getSelection ? function(te) {
10886 try { return te.selectionStart != te.selectionEnd; }
10887 catch(e) { return false; }
10889 try {var range = te.ownerDocument.selection.createRange();}
10891 if (!range || range.parentElement() != te) return false;
10892 return range.compareEndPoints("StartToEnd", range) != 0;
10895 var hasCopyEvent = (function() {
10896 var e = elt("div");
10897 if ("oncopy" in e) return true;
10898 e.setAttribute("oncopy", "return;");
10899 return typeof e.oncopy == "function";
10902 var badZoomedRects = null;
10903 function hasBadZoomedRects(measure) {
10904 if (badZoomedRects != null) return badZoomedRects;
10905 var node = removeChildrenAndAdd(measure, elt("span", "x"));
10906 var normal = node.getBoundingClientRect();
10907 var fromRange = range(node, 0, 1).getBoundingClientRect();
10908 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
10913 var keyNames = CodeMirror.keyNames = {
10914 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
10915 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
10916 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
10917 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
10918 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
10919 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
10920 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
10921 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
10925 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
10927 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
10929 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
10934 function iterateBidiSections(order, from, to, f) {
10935 if (!order) return f(from, to, "ltr");
10937 for (var i = 0; i < order.length; ++i) {
10938 var part = order[i];
10939 if (part.from < to && part.to > from || from == to && part.to == from) {
10940 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
10944 if (!found) f(from, to, "ltr");
10947 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
10948 function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
10950 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
10951 function lineRight(line) {
10952 var order = getOrder(line);
10953 if (!order) return line.text.length;
10954 return bidiRight(lst(order));
10957 function lineStart(cm, lineN) {
10958 var line = getLine(cm.doc, lineN);
10959 var visual = visualLine(line);
10960 if (visual != line) lineN = lineNo(visual);
10961 var order = getOrder(visual);
10962 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
10963 return Pos(lineN, ch);
10965 function lineEnd(cm, lineN) {
10966 var merged, line = getLine(cm.doc, lineN);
10967 while (merged = collapsedSpanAtEnd(line)) {
10968 line = merged.find(1, true).line;
10971 var order = getOrder(line);
10972 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
10973 return Pos(lineN == null ? lineNo(line) : lineN, ch);
10975 function lineStartSmart(cm, pos) {
10976 var start = lineStart(cm, pos.line);
10977 var line = getLine(cm.doc, start.line);
10978 var order = getOrder(line);
10979 if (!order || order[0].level == 0) {
10980 var firstNonWS = Math.max(0, line.text.search(/\S/));
10981 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
10982 return Pos(start.line, inWS ? 0 : firstNonWS);
10987 function compareBidiLevel(order, a, b) {
10988 var linedir = order[0].level;
10989 if (a == linedir) return true;
10990 if (b == linedir) return false;
10994 function getBidiPartAt(order, pos) {
10996 for (var i = 0, found; i < order.length; ++i) {
10997 var cur = order[i];
10998 if (cur.from < pos && cur.to > pos) return i;
10999 if ((cur.from == pos || cur.to == pos)) {
11000 if (found == null) {
11002 } else if (compareBidiLevel(order, cur.level, order[found].level)) {
11003 if (cur.from != cur.to) bidiOther = found;
11006 if (cur.from != cur.to) bidiOther = i;
11014 function moveInLine(line, pos, dir, byUnit) {
11015 if (!byUnit) return pos + dir;
11017 while (pos > 0 && isExtendingChar(line.text.charAt(pos)));
11021 // This is needed in order to move 'visually' through bi-directional
11022 // text -- i.e., pressing left should make the cursor go left, even
11023 // when in RTL text. The tricky part is the 'jumps', where RTL and
11024 // LTR text touch each other. This often requires the cursor offset
11025 // to move more than one unit, in order to visually move one unit.
11026 function moveVisually(line, start, dir, byUnit) {
11027 var bidi = getOrder(line);
11028 if (!bidi) return moveLogically(line, start, dir, byUnit);
11029 var pos = getBidiPartAt(bidi, start), part = bidi[pos];
11030 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
11033 if (target > part.from && target < part.to) return target;
11034 if (target == part.from || target == part.to) {
11035 if (getBidiPartAt(bidi, target) == pos) return target;
11036 part = bidi[pos += dir];
11037 return (dir > 0) == part.level % 2 ? part.to : part.from;
11039 part = bidi[pos += dir];
11040 if (!part) return null;
11041 if ((dir > 0) == part.level % 2)
11042 target = moveInLine(line, part.to, -1, byUnit);
11044 target = moveInLine(line, part.from, 1, byUnit);
11049 function moveLogically(line, start, dir, byUnit) {
11050 var target = start + dir;
11051 if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir;
11052 return target < 0 || target > line.text.length ? null : target;
11055 // Bidirectional ordering algorithm
11056 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
11057 // that this (partially) implements.
11059 // One-char codes used for character types:
11060 // L (L): Left-to-Right
11061 // R (R): Right-to-Left
11062 // r (AL): Right-to-Left Arabic
11063 // 1 (EN): European Number
11064 // + (ES): European Number Separator
11065 // % (ET): European Number Terminator
11066 // n (AN): Arabic Number
11067 // , (CS): Common Number Separator
11068 // m (NSM): Non-Spacing Mark
11069 // b (BN): Boundary Neutral
11070 // s (B): Paragraph Separator
11071 // t (S): Segment Separator
11072 // w (WS): Whitespace
11073 // N (ON): Other Neutrals
11075 // Returns null if characters are ordered as they appear
11076 // (left-to-right), or an array of sections ({from, to, level}
11077 // objects) in the order in which they occur visually.
11078 var bidiOrdering = (function() {
11079 // Character types for codepoints 0 to 0xff
11080 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
11081 // Character types for codepoints 0x600 to 0x6ff
11082 var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm";
11083 function charType(code) {
11084 if (code <= 0xf7) return lowTypes.charAt(code);
11085 else if (0x590 <= code && code <= 0x5f4) return "R";
11086 else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600);
11087 else if (0x6ee <= code && code <= 0x8ac) return "r";
11088 else if (0x2000 <= code && code <= 0x200b) return "w";
11089 else if (code == 0x200c) return "b";
11093 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
11094 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
11095 // Browsers seem to always treat the boundaries of block elements as being L.
11096 var outerType = "L";
11098 function BidiSpan(level, from, to) {
11099 this.level = level;
11100 this.from = from; this.to = to;
11103 return function(str) {
11104 if (!bidiRE.test(str)) return false;
11105 var len = str.length, types = [];
11106 for (var i = 0, type; i < len; ++i)
11107 types.push(type = charType(str.charCodeAt(i)));
11109 // W1. Examine each non-spacing mark (NSM) in the level run, and
11110 // change the type of the NSM to the type of the previous
11111 // character. If the NSM is at the start of the level run, it will
11112 // get the type of sor.
11113 for (var i = 0, prev = outerType; i < len; ++i) {
11114 var type = types[i];
11115 if (type == "m") types[i] = prev;
11119 // W2. Search backwards from each instance of a European number
11120 // until the first strong type (R, L, AL, or sor) is found. If an
11121 // AL is found, change the type of the European number to Arabic
11123 // W3. Change all ALs to R.
11124 for (var i = 0, cur = outerType; i < len; ++i) {
11125 var type = types[i];
11126 if (type == "1" && cur == "r") types[i] = "n";
11127 else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
11130 // W4. A single European separator between two European numbers
11131 // changes to a European number. A single common separator between
11132 // two numbers of the same type changes to that type.
11133 for (var i = 1, prev = types[0]; i < len - 1; ++i) {
11134 var type = types[i];
11135 if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
11136 else if (type == "," && prev == types[i+1] &&
11137 (prev == "1" || prev == "n")) types[i] = prev;
11141 // W5. A sequence of European terminators adjacent to European
11142 // numbers changes to all European numbers.
11143 // W6. Otherwise, separators and terminators change to Other
11145 for (var i = 0; i < len; ++i) {
11146 var type = types[i];
11147 if (type == ",") types[i] = "N";
11148 else if (type == "%") {
11149 for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
11150 var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
11151 for (var j = i; j < end; ++j) types[j] = replace;
11156 // W7. Search backwards from each instance of a European number
11157 // until the first strong type (R, L, or sor) is found. If an L is
11158 // found, then change the type of the European number to L.
11159 for (var i = 0, cur = outerType; i < len; ++i) {
11160 var type = types[i];
11161 if (cur == "L" && type == "1") types[i] = "L";
11162 else if (isStrong.test(type)) cur = type;
11165 // N1. A sequence of neutrals takes the direction of the
11166 // surrounding strong text if the text on both sides has the same
11167 // direction. European and Arabic numbers act as if they were R in
11168 // terms of their influence on neutrals. Start-of-level-run (sor)
11169 // and end-of-level-run (eor) are used at level run boundaries.
11170 // N2. Any remaining neutrals take the embedding direction.
11171 for (var i = 0; i < len; ++i) {
11172 if (isNeutral.test(types[i])) {
11173 for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
11174 var before = (i ? types[i-1] : outerType) == "L";
11175 var after = (end < len ? types[end] : outerType) == "L";
11176 var replace = before || after ? "L" : "R";
11177 for (var j = i; j < end; ++j) types[j] = replace;
11182 // Here we depart from the documented algorithm, in order to avoid
11183 // building up an actual levels array. Since there are only three
11184 // levels (0, 1, 2) in an implementation that doesn't take
11185 // explicit embedding into account, we can build up the order on
11186 // the fly, without following the level-based algorithm.
11188 for (var i = 0; i < len;) {
11189 if (countsAsLeft.test(types[i])) {
11191 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
11192 order.push(new BidiSpan(0, start, i));
11194 var pos = i, at = order.length;
11195 for (++i; i < len && types[i] != "L"; ++i) {}
11196 for (var j = pos; j < i;) {
11197 if (countsAsNum.test(types[j])) {
11198 if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j));
11200 for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
11201 order.splice(at, 0, new BidiSpan(2, nstart, j));
11205 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i));
11208 if (order[0].level == 1 && (m = str.match(/^\s+/))) {
11209 order[0].from = m[0].length;
11210 order.unshift(new BidiSpan(0, 0, m[0].length));
11212 if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
11213 lst(order).to -= m[0].length;
11214 order.push(new BidiSpan(0, len - m[0].length, len));
11216 if (order[0].level == 2)
11217 order.unshift(new BidiSpan(1, order[0].to, order[0].to));
11218 if (order[0].level != lst(order).level)
11219 order.push(new BidiSpan(order[0].level, len, len));
11227 CodeMirror.version = "5.15.2";
11232 },{}],11:[function(require,module,exports){
11233 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11234 // Distributed under an MIT license: http://codemirror.net/LICENSE
11237 if (typeof exports == "object" && typeof module == "object") // CommonJS
11238 mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay"));
11239 else if (typeof define == "function" && define.amd) // AMD
11240 define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod);
11241 else // Plain browser env
11243 })(function(CodeMirror) {
11246 var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i
11248 CodeMirror.defineMode("gfm", function(config, modeConfig) {
11250 function blankLine(state) {
11251 state.code = false;
11255 startState: function() {
11262 copyState: function(s) {
11265 codeBlock: s.codeBlock,
11266 ateSpace: s.ateSpace
11269 token: function(stream, state) {
11270 state.combineTokens = null;
11272 // Hack to prevent formatting override inside code blocks (block and inline)
11273 if (state.codeBlock) {
11274 if (stream.match(/^```+/)) {
11275 state.codeBlock = false;
11278 stream.skipToEnd();
11281 if (stream.sol()) {
11282 state.code = false;
11284 if (stream.sol() && stream.match(/^```+/)) {
11285 stream.skipToEnd();
11286 state.codeBlock = true;
11289 // If this block is changed, it may need to be updated in Markdown mode
11290 if (stream.peek() === '`') {
11292 var before = stream.pos;
11293 stream.eatWhile('`');
11294 var difference = 1 + stream.pos - before;
11296 codeDepth = difference;
11299 if (difference === codeDepth) { // Must be exact
11300 state.code = false;
11304 } else if (state.code) {
11308 // Check if space. If so, links can be formatted later on
11309 if (stream.eatSpace()) {
11310 state.ateSpace = true;
11313 if (stream.sol() || state.ateSpace) {
11314 state.ateSpace = false;
11315 if (modeConfig.gitHubSpice !== false) {
11316 if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
11317 // User/Project@SHA
11320 state.combineTokens = true;
11322 } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
11323 // User/Project#Num
11326 state.combineTokens = true;
11331 if (stream.match(urlRE) &&
11332 stream.string.slice(stream.start - 2, stream.start) != "](" &&
11333 (stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) {
11335 // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
11336 // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
11337 // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL
11338 state.combineTokens = true;
11344 blankLine: blankLine
11347 var markdownConfig = {
11348 underscoresBreakWords: false,
11350 fencedCodeBlocks: '```',
11351 strikethrough: true
11353 for (var attr in modeConfig) {
11354 markdownConfig[attr] = modeConfig[attr];
11356 markdownConfig.name = "markdown";
11357 return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay);
11361 CodeMirror.defineMIME("text/x-gfm", "gfm");
11364 },{"../../addon/mode/overlay":8,"../../lib/codemirror":10,"../markdown/markdown":12}],12:[function(require,module,exports){
11365 // CodeMirror, copyright (c) by Marijn Haverbeke and others
11366 // Distributed under an MIT license: http://codemirror.net/LICENSE
11369 if (typeof exports == "object" && typeof module == "object") // CommonJS
11370 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
11371 else if (typeof define == "function" && define.amd) // AMD
11372 define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
11373 else // Plain browser env
11375 })(function(CodeMirror) {
11378 CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
11380 var htmlMode = CodeMirror.getMode(cmCfg, "text/html");
11381 var htmlModeMissing = htmlMode.name == "null"
11383 function getMode(name) {
11384 if (CodeMirror.findModeByName) {
11385 var found = CodeMirror.findModeByName(name);
11386 if (found) name = found.mime || found.mimes[0];
11388 var mode = CodeMirror.getMode(cmCfg, name);
11389 return mode.name == "null" ? null : mode;
11392 // Should characters that affect highlighting be highlighted separate?
11393 // Does not include characters that will be output (such as `1.` and `-` for lists)
11394 if (modeCfg.highlightFormatting === undefined)
11395 modeCfg.highlightFormatting = false;
11397 // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
11398 // Excess `>` will emit `error` token.
11399 if (modeCfg.maxBlockquoteDepth === undefined)
11400 modeCfg.maxBlockquoteDepth = 0;
11402 // Should underscores in words open/close em/strong?
11403 if (modeCfg.underscoresBreakWords === undefined)
11404 modeCfg.underscoresBreakWords = true;
11406 // Use `fencedCodeBlocks` to configure fenced code blocks. false to
11407 // disable, string to specify a precise regexp that the fence should
11408 // match, and true to allow three or more backticks or tildes (as
11409 // per CommonMark).
11411 // Turn on task lists? ("- [ ] " and "- [x] ")
11412 if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
11414 // Turn on strikethrough syntax
11415 if (modeCfg.strikethrough === undefined)
11416 modeCfg.strikethrough = false;
11418 // Allow token types to be overridden by user-provided token types.
11419 if (modeCfg.tokenTypeOverrides === undefined)
11420 modeCfg.tokenTypeOverrides = {};
11426 list1: "variable-2",
11427 list2: "variable-3",
11431 formatting: "formatting",
11432 linkInline: "link",
11435 linkHref: "string",
11438 strikethrough: "strikethrough"
11441 for (var tokenType in tokenTypes) {
11442 if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {
11443 tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];
11447 var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
11448 , ulRE = /^[*\-+]\s+/
11449 , olRE = /^[0-9]+([.)])\s+/
11450 , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
11451 , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
11452 , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
11453 , textRE = /^[^#!\[\]*_\\<>` "'(~]+/
11454 , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
11455 ")[ \\t]*([\\w+#\-]*)");
11457 function switchInline(stream, state, f) {
11458 state.f = state.inline = f;
11459 return f(stream, state);
11462 function switchBlock(stream, state, f) {
11463 state.f = state.block = f;
11464 return f(stream, state);
11467 function lineIsEmpty(line) {
11468 return !line || !/\S/.test(line.string)
11473 function blankLine(state) {
11474 // Reset linkTitle state
11475 state.linkTitle = false;
11478 // Reset STRONG state
11479 state.strong = false;
11480 // Reset strikethrough state
11481 state.strikethrough = false;
11482 // Reset state.quote
11484 // Reset state.indentedCode
11485 state.indentedCode = false;
11486 if (htmlModeMissing && state.f == htmlBlock) {
11487 state.f = inlineNormal;
11488 state.block = blockNormal;
11490 // Reset state.trailingSpace
11491 state.trailingSpace = 0;
11492 state.trailingSpaceNewLine = false;
11493 // Mark this line as blank
11494 state.prevLine = state.thisLine
11495 state.thisLine = null
11499 function blockNormal(stream, state) {
11501 var sol = stream.sol();
11503 var prevLineIsList = state.list !== false,
11504 prevLineIsIndentedCode = state.indentedCode;
11506 state.indentedCode = false;
11508 if (prevLineIsList) {
11509 if (state.indentationDiff >= 0) { // Continued list
11510 if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
11511 state.indentation -= state.indentationDiff;
11514 } else if (state.indentation > 0) {
11516 } else { // No longer a list
11517 state.list = false;
11522 if (state.indentationDiff >= 4) {
11523 stream.skipToEnd();
11524 if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
11525 state.indentation -= 4;
11526 state.indentedCode = true;
11527 return tokenTypes.code;
11531 } else if (stream.eatSpace()) {
11533 } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
11534 state.header = match[1].length;
11535 if (modeCfg.highlightFormatting) state.formatting = "header";
11536 state.f = state.inline;
11537 return getType(state);
11538 } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
11539 !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
11540 state.header = match[0].charAt(0) == '=' ? 1 : 2;
11541 if (modeCfg.highlightFormatting) state.formatting = "header";
11542 state.f = state.inline;
11543 return getType(state);
11544 } else if (stream.eat('>')) {
11545 state.quote = sol ? 1 : state.quote + 1;
11546 if (modeCfg.highlightFormatting) state.formatting = "quote";
11548 return getType(state);
11549 } else if (stream.peek() === '[') {
11550 return switchInline(stream, state, footnoteLink);
11551 } else if (stream.match(hrRE, true)) {
11553 return tokenTypes.hr;
11554 } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
11555 var listType = null;
11556 if (stream.match(ulRE, true)) {
11559 stream.match(olRE, true);
11562 state.indentation = stream.column() + stream.current().length;
11565 // While this list item's marker's indentation
11566 // is less than the deepest list item's content's indentation,
11567 // pop the deepest list item indentation off the stack.
11568 while (state.listStack && stream.column() < state.listStack[state.listStack.length - 1]) {
11569 state.listStack.pop();
11572 // Add this list item's content's indentation to the stack
11573 state.listStack.push(state.indentation);
11575 if (modeCfg.taskLists && stream.match(taskListRE, false)) {
11576 state.taskList = true;
11578 state.f = state.inline;
11579 if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
11580 return getType(state);
11581 } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
11582 state.fencedChars = match[1]
11583 // try switching mode
11584 state.localMode = getMode(match[2]);
11585 if (state.localMode) state.localState = CodeMirror.startState(state.localMode);
11586 state.f = state.block = local;
11587 if (modeCfg.highlightFormatting) state.formatting = "code-block";
11589 return getType(state);
11592 return switchInline(stream, state, state.inline);
11595 function htmlBlock(stream, state) {
11596 var style = htmlMode.token(stream, state.htmlState);
11597 if (!htmlModeMissing) {
11598 var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
11599 if ((inner.mode.name == "xml" && inner.state.tagStart === null &&
11600 (!inner.state.context && inner.state.tokenize.isInText)) ||
11601 (state.md_inside && stream.current().indexOf(">") > -1)) {
11602 state.f = inlineNormal;
11603 state.block = blockNormal;
11604 state.htmlState = null;
11610 function local(stream, state) {
11611 if (state.fencedChars && stream.match(state.fencedChars, false)) {
11612 state.localMode = state.localState = null;
11613 state.f = state.block = leavingLocal;
11615 } else if (state.localMode) {
11616 return state.localMode.token(stream, state.localState);
11618 stream.skipToEnd();
11619 return tokenTypes.code;
11623 function leavingLocal(stream, state) {
11624 stream.match(state.fencedChars);
11625 state.block = blockNormal;
11626 state.f = inlineNormal;
11627 state.fencedChars = null;
11628 if (modeCfg.highlightFormatting) state.formatting = "code-block";
11630 var returnType = getType(state);
11636 function getType(state) {
11639 if (state.formatting) {
11640 styles.push(tokenTypes.formatting);
11642 if (typeof state.formatting === "string") state.formatting = [state.formatting];
11644 for (var i = 0; i < state.formatting.length; i++) {
11645 styles.push(tokenTypes.formatting + "-" + state.formatting[i]);
11647 if (state.formatting[i] === "header") {
11648 styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header);
11651 // Add `formatting-quote` and `formatting-quote-#` for blockquotes
11652 // Add `error` instead if the maximum blockquote nesting depth is passed
11653 if (state.formatting[i] === "quote") {
11654 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
11655 styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote);
11657 styles.push("error");
11663 if (state.taskOpen) {
11664 styles.push("meta");
11665 return styles.length ? styles.join(' ') : null;
11667 if (state.taskClosed) {
11668 styles.push("property");
11669 return styles.length ? styles.join(' ') : null;
11672 if (state.linkHref) {
11673 styles.push(tokenTypes.linkHref, "url");
11674 } else { // Only apply inline styles to non-url text
11675 if (state.strong) { styles.push(tokenTypes.strong); }
11676 if (state.em) { styles.push(tokenTypes.em); }
11677 if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
11678 if (state.linkText) { styles.push(tokenTypes.linkText); }
11679 if (state.code) { styles.push(tokenTypes.code); }
11682 if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
11685 styles.push(tokenTypes.quote);
11687 // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
11688 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
11689 styles.push(tokenTypes.quote + "-" + state.quote);
11691 styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth);
11695 if (state.list !== false) {
11696 var listMod = (state.listStack.length - 1) % 3;
11698 styles.push(tokenTypes.list1);
11699 } else if (listMod === 1) {
11700 styles.push(tokenTypes.list2);
11702 styles.push(tokenTypes.list3);
11706 if (state.trailingSpaceNewLine) {
11707 styles.push("trailing-space-new-line");
11708 } else if (state.trailingSpace) {
11709 styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
11712 return styles.length ? styles.join(' ') : null;
11715 function handleText(stream, state) {
11716 if (stream.match(textRE, true)) {
11717 return getType(state);
11722 function inlineNormal(stream, state) {
11723 var style = state.text(stream, state);
11724 if (typeof style !== 'undefined')
11727 if (state.list) { // List marker (*, +, -, 1., etc)
11729 return getType(state);
11732 if (state.taskList) {
11733 var taskOpen = stream.match(taskListRE, true)[1] !== "x";
11734 if (taskOpen) state.taskOpen = true;
11735 else state.taskClosed = true;
11736 if (modeCfg.highlightFormatting) state.formatting = "task";
11737 state.taskList = false;
11738 return getType(state);
11741 state.taskOpen = false;
11742 state.taskClosed = false;
11744 if (state.header && stream.match(/^#+$/, true)) {
11745 if (modeCfg.highlightFormatting) state.formatting = "header";
11746 return getType(state);
11749 // Get sol() value now, before character is consumed
11750 var sol = stream.sol();
11752 var ch = stream.next();
11754 // Matches link titles present on next line
11755 if (state.linkTitle) {
11756 state.linkTitle = false;
11761 matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
11762 var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
11763 if (stream.match(new RegExp(regex), true)) {
11764 return tokenTypes.linkHref;
11768 // If this block is changed, it may need to be updated in GFM mode
11770 var previousFormatting = state.formatting;
11771 if (modeCfg.highlightFormatting) state.formatting = "code";
11772 stream.eatWhile('`');
11773 var count = stream.current().length
11774 if (state.code == 0) {
11776 return getType(state)
11777 } else if (count == state.code) { // Must be exact
11778 var t = getType(state)
11782 state.formatting = previousFormatting
11783 return getType(state)
11785 } else if (state.code) {
11786 return getType(state);
11791 if (modeCfg.highlightFormatting) {
11792 var type = getType(state);
11793 var formattingEscape = tokenTypes.formatting + "-escape";
11794 return type ? type + " " + formattingEscape : formattingEscape;
11798 if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
11799 stream.match(/\[[^\]]*\]/);
11800 state.inline = state.f = linkHref;
11801 return tokenTypes.image;
11804 if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false)) {
11805 state.linkText = true;
11806 if (modeCfg.highlightFormatting) state.formatting = "link";
11807 return getType(state);
11810 if (ch === ']' && state.linkText && stream.match(/\(.*?\)| ?\[.*?\]/, false)) {
11811 if (modeCfg.highlightFormatting) state.formatting = "link";
11812 var type = getType(state);
11813 state.linkText = false;
11814 state.inline = state.f = linkHref;
11818 if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
11819 state.f = state.inline = linkInline;
11820 if (modeCfg.highlightFormatting) state.formatting = "link";
11821 var type = getType(state);
11827 return type + tokenTypes.linkInline;
11830 if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
11831 state.f = state.inline = linkInline;
11832 if (modeCfg.highlightFormatting) state.formatting = "link";
11833 var type = getType(state);
11839 return type + tokenTypes.linkEmail;
11842 if (ch === '<' && stream.match(/^(!--|\w)/, false)) {
11843 var end = stream.string.indexOf(">", stream.pos);
11845 var atts = stream.string.substring(stream.start, end);
11846 if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
11849 state.htmlState = CodeMirror.startState(htmlMode);
11850 return switchBlock(stream, state, htmlBlock);
11853 if (ch === '<' && stream.match(/^\/\w*?>/)) {
11854 state.md_inside = false;
11858 var ignoreUnderscore = false;
11859 if (!modeCfg.underscoresBreakWords) {
11860 if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
11861 var prevPos = stream.pos - 2;
11862 if (prevPos >= 0) {
11863 var prevCh = stream.string.charAt(prevPos);
11864 if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
11865 ignoreUnderscore = true;
11870 if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
11871 if (sol && stream.peek() === ' ') {
11872 // Do nothing, surrounded by newline and space
11873 } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
11874 if (modeCfg.highlightFormatting) state.formatting = "strong";
11875 var t = getType(state);
11876 state.strong = false;
11878 } else if (!state.strong && stream.eat(ch)) { // Add STRONG
11880 if (modeCfg.highlightFormatting) state.formatting = "strong";
11881 return getType(state);
11882 } else if (state.em === ch) { // Remove EM
11883 if (modeCfg.highlightFormatting) state.formatting = "em";
11884 var t = getType(state);
11887 } else if (!state.em) { // Add EM
11889 if (modeCfg.highlightFormatting) state.formatting = "em";
11890 return getType(state);
11892 } else if (ch === ' ') {
11893 if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
11894 if (stream.peek() === ' ') { // Surrounded by spaces, ignore
11895 return getType(state);
11896 } else { // Not surrounded by spaces, back up pointer
11902 if (modeCfg.strikethrough) {
11903 if (ch === '~' && stream.eatWhile(ch)) {
11904 if (state.strikethrough) {// Remove strikethrough
11905 if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
11906 var t = getType(state);
11907 state.strikethrough = false;
11909 } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
11910 state.strikethrough = true;
11911 if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
11912 return getType(state);
11914 } else if (ch === ' ') {
11915 if (stream.match(/^~~/, true)) { // Probably surrounded by space
11916 if (stream.peek() === ' ') { // Surrounded by spaces, ignore
11917 return getType(state);
11918 } else { // Not surrounded by spaces, back up pointer
11926 if (stream.match(/ +$/, false)) {
11927 state.trailingSpace++;
11928 } else if (state.trailingSpace) {
11929 state.trailingSpaceNewLine = true;
11933 return getType(state);
11936 function linkInline(stream, state) {
11937 var ch = stream.next();
11940 state.f = state.inline = inlineNormal;
11941 if (modeCfg.highlightFormatting) state.formatting = "link";
11942 var type = getType(state);
11948 return type + tokenTypes.linkInline;
11951 stream.match(/^[^>]+/, true);
11953 return tokenTypes.linkInline;
11956 function linkHref(stream, state) {
11957 // Check if space, and return NULL if so (to avoid marking the space)
11958 if(stream.eatSpace()){
11961 var ch = stream.next();
11962 if (ch === '(' || ch === '[') {
11963 state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]", 0);
11964 if (modeCfg.highlightFormatting) state.formatting = "link-string";
11965 state.linkHref = true;
11966 return getType(state);
11972 ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,
11973 "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\\]]|\\.)*\])*?(?=\])/
11976 function getLinkHrefInside(endChar) {
11977 return function(stream, state) {
11978 var ch = stream.next();
11980 if (ch === endChar) {
11981 state.f = state.inline = inlineNormal;
11982 if (modeCfg.highlightFormatting) state.formatting = "link-string";
11983 var returnState = getType(state);
11984 state.linkHref = false;
11985 return returnState;
11988 stream.match(linkRE[endChar])
11989 state.linkHref = true;
11990 return getType(state);
11994 function footnoteLink(stream, state) {
11995 if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
11996 state.f = footnoteLinkInside;
11997 stream.next(); // Consume [
11998 if (modeCfg.highlightFormatting) state.formatting = "link";
11999 state.linkText = true;
12000 return getType(state);
12002 return switchInline(stream, state, inlineNormal);
12005 function footnoteLinkInside(stream, state) {
12006 if (stream.match(/^\]:/, true)) {
12007 state.f = state.inline = footnoteUrl;
12008 if (modeCfg.highlightFormatting) state.formatting = "link";
12009 var returnType = getType(state);
12010 state.linkText = false;
12014 stream.match(/^([^\]\\]|\\.)+/, true);
12016 return tokenTypes.linkText;
12019 function footnoteUrl(stream, state) {
12020 // Check if space, and return NULL if so (to avoid marking the space)
12021 if(stream.eatSpace()){
12025 stream.match(/^[^\s]+/, true);
12026 // Check for link title
12027 if (stream.peek() === undefined) { // End of line, set flag to check next line
12028 state.linkTitle = true;
12029 } else { // More content on line, check if link title
12030 stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
12032 state.f = state.inline = inlineNormal;
12033 return tokenTypes.linkHref + " url";
12037 startState: function() {
12044 block: blockNormal,
12048 inline: inlineNormal,
12065 trailingSpaceNewLine: false,
12066 strikethrough: false,
12071 copyState: function(s) {
12075 prevLine: s.prevLine,
12076 thisLine: s.thisLine,
12079 htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
12080 indentation: s.indentation,
12082 localMode: s.localMode,
12083 localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
12088 linkTitle: s.linkTitle,
12092 strikethrough: s.strikethrough,
12095 taskList: s.taskList,
12097 listStack: s.listStack.slice(0),
12099 indentedCode: s.indentedCode,
12100 trailingSpace: s.trailingSpace,
12101 trailingSpaceNewLine: s.trailingSpaceNewLine,
12102 md_inside: s.md_inside,
12103 fencedChars: s.fencedChars
12107 token: function(stream, state) {
12109 // Reset state.formatting
12110 state.formatting = false;
12112 if (stream != state.thisLine) {
12113 var forceBlankLine = state.header || state.hr;
12115 // Reset state.header and state.hr
12119 if (stream.match(/^\s*$/, true) || forceBlankLine) {
12121 if (!forceBlankLine) return null
12122 state.prevLine = null
12125 state.prevLine = state.thisLine
12126 state.thisLine = stream
12128 // Reset state.taskList
12129 state.taskList = false;
12131 // Reset state.trailingSpace
12132 state.trailingSpace = 0;
12133 state.trailingSpaceNewLine = false;
12135 state.f = state.block;
12136 var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
12137 state.indentationDiff = Math.min(indentation - state.indentation, 4);
12138 state.indentation = state.indentation + state.indentationDiff;
12139 if (indentation > 0) return null;
12141 return state.f(stream, state);
12144 innerMode: function(state) {
12145 if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
12146 if (state.localState) return {state: state.localState, mode: state.localMode};
12147 return {state: state, mode: mode};
12150 blankLine: blankLine,
12159 CodeMirror.defineMIME("text/x-markdown", "markdown");
12163 },{"../../lib/codemirror":10,"../meta":13,"../xml/xml":14}],13:[function(require,module,exports){
12164 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12165 // Distributed under an MIT license: http://codemirror.net/LICENSE
12168 if (typeof exports == "object" && typeof module == "object") // CommonJS
12169 mod(require("../lib/codemirror"));
12170 else if (typeof define == "function" && define.amd) // AMD
12171 define(["../lib/codemirror"], mod);
12172 else // Plain browser env
12174 })(function(CodeMirror) {
12177 CodeMirror.modeInfo = [
12178 {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
12179 {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
12180 {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]},
12181 {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
12182 {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]},
12183 {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
12184 {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
12185 {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
12186 {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
12187 {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj", "cljc", "cljx"]},
12188 {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]},
12189 {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
12190 {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
12191 {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
12192 {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
12193 {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
12194 {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
12195 {name: "Crystal", mime: "text/x-crystal", mode: "crystal", ext: ["cr"]},
12196 {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
12197 {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
12198 {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
12199 {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]},
12200 {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]},
12201 {name: "Django", mime: "text/x-django", mode: "django"},
12202 {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/},
12203 {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]},
12204 {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]},
12205 {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
12206 {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
12207 {name: "edn", mime: "application/edn", mode: "clojure", ext: ["edn"]},
12208 {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
12209 {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
12210 {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
12211 {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
12212 {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
12213 {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
12214 {name: "FCL", mime: "text/x-fcl", mode: "fcl"},
12215 {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
12216 {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
12217 {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
12218 {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
12219 {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
12220 {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
12221 {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
12222 {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"]},
12223 {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
12224 {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
12225 {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]},
12226 {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
12227 {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
12228 {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
12229 {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
12230 {name: "HTTP", mime: "message/http", mode: "http"},
12231 {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
12232 {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
12233 {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
12234 {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
12235 {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
12236 mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
12237 {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
12238 {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
12239 {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]},
12240 {name: "Jinja2", mime: "null", mode: "jinja2"},
12241 {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
12242 {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
12243 {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
12244 {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]},
12245 {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]},
12246 {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
12247 {name: "mIRC", mime: "text/mirc", mode: "mirc"},
12248 {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
12249 {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
12250 {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
12251 {name: "MUMPS", mime: "text/x-mumps", mode: "mumps", ext: ["mps"]},
12252 {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
12253 {name: "mbox", mime: "application/mbox", mode: "mbox", ext: ["mbox"]},
12254 {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
12255 {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
12256 {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]},
12257 {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
12258 {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"], alias: ["objective-c", "objc"]},
12259 {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
12260 {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
12261 {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]},
12262 {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
12263 {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
12264 {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
12265 {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
12266 {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
12267 {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
12268 {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
12269 {name: "PowerShell", mime: "application/x-powershell", mode: "powershell", ext: ["ps1", "psd1", "psm1"]},
12270 {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
12271 {name: "ProtoBuf", mime: "text/x-protobuf", mode: "protobuf", ext: ["proto"]},
12272 {name: "Python", mime: "text/x-python", mode: "python", ext: ["BUILD", "bzl", "py", "pyw"], file: /^(BUCK|BUILD)$/},
12273 {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
12274 {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
12275 {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
12276 {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
12277 {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
12278 {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
12279 {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]},
12280 {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]},
12281 {name: "SAS", mime: "text/x-sas", mode: "sas", ext: ["sas"]},
12282 {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]},
12283 {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
12284 {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
12285 {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
12286 {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/},
12287 {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
12288 {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
12289 {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
12290 {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
12291 {name: "Solr", mime: "text/x-solr", mode: "solr"},
12292 {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
12293 {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
12294 {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
12295 {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
12296 {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]},
12297 {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
12298 {name: "sTeX", mime: "text/x-stex", mode: "stex"},
12299 {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
12300 {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
12301 {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
12302 {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
12303 {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
12304 {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"},
12305 {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
12306 {name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
12307 {name: "troff", mime: "text/troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
12308 {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
12309 {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
12310 {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
12311 {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
12312 {name: "Twig", mime: "text/x-twig", mode: "twig"},
12313 {name: "Web IDL", mime: "text/x-webidl", mode: "webidl", ext: ["webidl"]},
12314 {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
12315 {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
12316 {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
12317 {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
12318 {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]},
12319 {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
12320 {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
12321 {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]},
12322 {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
12323 {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]},
12324 {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]},
12325 {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]},
12326 {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]}
12328 // Ensure all modes have a mime property for backwards compatibility
12329 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
12330 var info = CodeMirror.modeInfo[i];
12331 if (info.mimes) info.mime = info.mimes[0];
12334 CodeMirror.findModeByMIME = function(mime) {
12335 mime = mime.toLowerCase();
12336 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
12337 var info = CodeMirror.modeInfo[i];
12338 if (info.mime == mime) return info;
12339 if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
12340 if (info.mimes[j] == mime) return info;
12344 CodeMirror.findModeByExtension = function(ext) {
12345 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
12346 var info = CodeMirror.modeInfo[i];
12347 if (info.ext) for (var j = 0; j < info.ext.length; j++)
12348 if (info.ext[j] == ext) return info;
12352 CodeMirror.findModeByFileName = function(filename) {
12353 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
12354 var info = CodeMirror.modeInfo[i];
12355 if (info.file && info.file.test(filename)) return info;
12357 var dot = filename.lastIndexOf(".");
12358 var ext = dot > -1 && filename.substring(dot + 1, filename.length);
12359 if (ext) return CodeMirror.findModeByExtension(ext);
12362 CodeMirror.findModeByName = function(name) {
12363 name = name.toLowerCase();
12364 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
12365 var info = CodeMirror.modeInfo[i];
12366 if (info.name.toLowerCase() == name) return info;
12367 if (info.alias) for (var j = 0; j < info.alias.length; j++)
12368 if (info.alias[j].toLowerCase() == name) return info;
12373 },{"../lib/codemirror":10}],14:[function(require,module,exports){
12374 // CodeMirror, copyright (c) by Marijn Haverbeke and others
12375 // Distributed under an MIT license: http://codemirror.net/LICENSE
12378 if (typeof exports == "object" && typeof module == "object") // CommonJS
12379 mod(require("../../lib/codemirror"));
12380 else if (typeof define == "function" && define.amd) // AMD
12381 define(["../../lib/codemirror"], mod);
12382 else // Plain browser env
12384 })(function(CodeMirror) {
12388 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
12389 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
12390 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
12391 'track': true, 'wbr': true, 'menuitem': true},
12392 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
12393 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
12394 'th': true, 'tr': true},
12396 'dd': {'dd': true, 'dt': true},
12397 'dt': {'dd': true, 'dt': true},
12398 'li': {'li': true},
12399 'option': {'option': true, 'optgroup': true},
12400 'optgroup': {'optgroup': true},
12401 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
12402 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
12403 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
12404 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
12405 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
12406 'rp': {'rp': true, 'rt': true},
12407 'rt': {'rp': true, 'rt': true},
12408 'tbody': {'tbody': true, 'tfoot': true},
12409 'td': {'td': true, 'th': true},
12410 'tfoot': {'tbody': true},
12411 'th': {'td': true, 'th': true},
12412 'thead': {'tbody': true, 'tfoot': true},
12415 doNotIndent: {"pre": true},
12416 allowUnquoted: true,
12417 allowMissing: true,
12422 autoSelfClosers: {},
12423 implicitlyClosed: {},
12424 contextGrabbers: {},
12426 allowUnquoted: false,
12427 allowMissing: false,
12431 CodeMirror.defineMode("xml", function(editorConf, config_) {
12432 var indentUnit = editorConf.indentUnit
12434 var defaults = config_.htmlMode ? htmlConfig : xmlConfig
12435 for (var prop in defaults) config[prop] = defaults[prop]
12436 for (var prop in config_) config[prop] = config_[prop]
12438 // Return variables for tokenizers
12439 var type, setStyle;
12441 function inText(stream, state) {
12442 function chain(parser) {
12443 state.tokenize = parser;
12444 return parser(stream, state);
12447 var ch = stream.next();
12449 if (stream.eat("!")) {
12450 if (stream.eat("[")) {
12451 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
12453 } else if (stream.match("--")) {
12454 return chain(inBlock("comment", "-->"));
12455 } else if (stream.match("DOCTYPE", true, true)) {
12456 stream.eatWhile(/[\w\._\-]/);
12457 return chain(doctype(1));
12461 } else if (stream.eat("?")) {
12462 stream.eatWhile(/[\w\._\-]/);
12463 state.tokenize = inBlock("meta", "?>");
12466 type = stream.eat("/") ? "closeTag" : "openTag";
12467 state.tokenize = inTag;
12468 return "tag bracket";
12470 } else if (ch == "&") {
12472 if (stream.eat("#")) {
12473 if (stream.eat("x")) {
12474 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
12476 ok = stream.eatWhile(/[\d]/) && stream.eat(";");
12479 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
12481 return ok ? "atom" : "error";
12483 stream.eatWhile(/[^&<]/);
12487 inText.isInText = true;
12489 function inTag(stream, state) {
12490 var ch = stream.next();
12491 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
12492 state.tokenize = inText;
12493 type = ch == ">" ? "endTag" : "selfcloseTag";
12494 return "tag bracket";
12495 } else if (ch == "=") {
12498 } else if (ch == "<") {
12499 state.tokenize = inText;
12500 state.state = baseState;
12501 state.tagName = state.tagStart = null;
12502 var next = state.tokenize(stream, state);
12503 return next ? next + " tag error" : "tag error";
12504 } else if (/[\'\"]/.test(ch)) {
12505 state.tokenize = inAttribute(ch);
12506 state.stringStartCol = stream.column();
12507 return state.tokenize(stream, state);
12509 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
12514 function inAttribute(quote) {
12515 var closure = function(stream, state) {
12516 while (!stream.eol()) {
12517 if (stream.next() == quote) {
12518 state.tokenize = inTag;
12524 closure.isInAttribute = true;
12528 function inBlock(style, terminator) {
12529 return function(stream, state) {
12530 while (!stream.eol()) {
12531 if (stream.match(terminator)) {
12532 state.tokenize = inText;
12540 function doctype(depth) {
12541 return function(stream, state) {
12543 while ((ch = stream.next()) != null) {
12545 state.tokenize = doctype(depth + 1);
12546 return state.tokenize(stream, state);
12547 } else if (ch == ">") {
12549 state.tokenize = inText;
12552 state.tokenize = doctype(depth - 1);
12553 return state.tokenize(stream, state);
12561 function Context(state, tagName, startOfLine) {
12562 this.prev = state.context;
12563 this.tagName = tagName;
12564 this.indent = state.indented;
12565 this.startOfLine = startOfLine;
12566 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
12567 this.noIndent = true;
12569 function popContext(state) {
12570 if (state.context) state.context = state.context.prev;
12572 function maybePopContext(state, nextTagName) {
12575 if (!state.context) {
12578 parentTagName = state.context.tagName;
12579 if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
12580 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
12587 function baseState(type, stream, state) {
12588 if (type == "openTag") {
12589 state.tagStart = stream.column();
12590 return tagNameState;
12591 } else if (type == "closeTag") {
12592 return closeTagNameState;
12597 function tagNameState(type, stream, state) {
12598 if (type == "word") {
12599 state.tagName = stream.current();
12603 setStyle = "error";
12604 return tagNameState;
12607 function closeTagNameState(type, stream, state) {
12608 if (type == "word") {
12609 var tagName = stream.current();
12610 if (state.context && state.context.tagName != tagName &&
12611 config.implicitlyClosed.hasOwnProperty(state.context.tagName))
12613 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
12617 setStyle = "tag error";
12618 return closeStateErr;
12621 setStyle = "error";
12622 return closeStateErr;
12626 function closeState(type, _stream, state) {
12627 if (type != "endTag") {
12628 setStyle = "error";
12634 function closeStateErr(type, stream, state) {
12635 setStyle = "error";
12636 return closeState(type, stream, state);
12639 function attrState(type, _stream, state) {
12640 if (type == "word") {
12641 setStyle = "attribute";
12642 return attrEqState;
12643 } else if (type == "endTag" || type == "selfcloseTag") {
12644 var tagName = state.tagName, tagStart = state.tagStart;
12645 state.tagName = state.tagStart = null;
12646 if (type == "selfcloseTag" ||
12647 config.autoSelfClosers.hasOwnProperty(tagName)) {
12648 maybePopContext(state, tagName);
12650 maybePopContext(state, tagName);
12651 state.context = new Context(state, tagName, tagStart == state.indented);
12655 setStyle = "error";
12658 function attrEqState(type, stream, state) {
12659 if (type == "equals") return attrValueState;
12660 if (!config.allowMissing) setStyle = "error";
12661 return attrState(type, stream, state);
12663 function attrValueState(type, stream, state) {
12664 if (type == "string") return attrContinuedState;
12665 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
12666 setStyle = "error";
12667 return attrState(type, stream, state);
12669 function attrContinuedState(type, stream, state) {
12670 if (type == "string") return attrContinuedState;
12671 return attrState(type, stream, state);
12675 startState: function(baseIndent) {
12676 var state = {tokenize: inText,
12678 indented: baseIndent || 0,
12679 tagName: null, tagStart: null,
12681 if (baseIndent != null) state.baseIndent = baseIndent
12685 token: function(stream, state) {
12686 if (!state.tagName && stream.sol())
12687 state.indented = stream.indentation();
12689 if (stream.eatSpace()) return null;
12691 var style = state.tokenize(stream, state);
12692 if ((style || type) && style != "comment") {
12694 state.state = state.state(type || style, stream, state);
12696 style = setStyle == "error" ? style + " error" : setStyle;
12701 indent: function(state, textAfter, fullLine) {
12702 var context = state.context;
12703 // Indent multi-line strings (e.g. css).
12704 if (state.tokenize.isInAttribute) {
12705 if (state.tagStart == state.indented)
12706 return state.stringStartCol + 1;
12708 return state.indented + indentUnit;
12710 if (context && context.noIndent) return CodeMirror.Pass;
12711 if (state.tokenize != inTag && state.tokenize != inText)
12712 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
12713 // Indent the starts of attribute names.
12714 if (state.tagName) {
12715 if (config.multilineTagIndentPastTag !== false)
12716 return state.tagStart + state.tagName.length + 2;
12718 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
12720 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
12721 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
12722 if (tagAfter && tagAfter[1]) { // Closing tag spotted
12724 if (context.tagName == tagAfter[2]) {
12725 context = context.prev;
12727 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
12728 context = context.prev;
12733 } else if (tagAfter) { // Opening tag spotted
12735 var grabbers = config.contextGrabbers[context.tagName];
12736 if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
12737 context = context.prev;
12742 while (context && context.prev && !context.startOfLine)
12743 context = context.prev;
12744 if (context) return context.indent + indentUnit;
12745 else return state.baseIndent || 0;
12748 electricInput: /<\/[\s\w:]+>$/,
12749 blockCommentStart: "<!--",
12750 blockCommentEnd: "-->",
12752 configuration: config.htmlMode ? "html" : "xml",
12753 helperType: config.htmlMode ? "html" : "xml",
12755 skipAttribute: function(state) {
12756 if (state.state == attrValueState)
12757 state.state = attrState
12762 CodeMirror.defineMIME("text/xml", "xml");
12763 CodeMirror.defineMIME("application/xml", "xml");
12764 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
12765 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
12769 },{"../../lib/codemirror":10}],15:[function(require,module,exports){
12770 exports.read = function (buffer, offset, isLE, mLen, nBytes) {
12772 var eLen = nBytes * 8 - mLen - 1
12773 var eMax = (1 << eLen) - 1
12774 var eBias = eMax >> 1
12776 var i = isLE ? (nBytes - 1) : 0
12777 var d = isLE ? -1 : 1
12778 var s = buffer[offset + i]
12782 e = s & ((1 << (-nBits)) - 1)
12785 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
12787 m = e & ((1 << (-nBits)) - 1)
12790 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
12794 } else if (e === eMax) {
12795 return m ? NaN : ((s ? -1 : 1) * Infinity)
12797 m = m + Math.pow(2, mLen)
12800 return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
12803 exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
12805 var eLen = nBytes * 8 - mLen - 1
12806 var eMax = (1 << eLen) - 1
12807 var eBias = eMax >> 1
12808 var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
12809 var i = isLE ? 0 : (nBytes - 1)
12810 var d = isLE ? 1 : -1
12811 var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
12813 value = Math.abs(value)
12815 if (isNaN(value) || value === Infinity) {
12816 m = isNaN(value) ? 1 : 0
12819 e = Math.floor(Math.log(value) / Math.LN2)
12820 if (value * (c = Math.pow(2, -e)) < 1) {
12824 if (e + eBias >= 1) {
12827 value += rt * Math.pow(2, 1 - eBias)
12829 if (value * c >= 2) {
12834 if (e + eBias >= eMax) {
12837 } else if (e + eBias >= 1) {
12838 m = (value * c - 1) * Math.pow(2, mLen)
12841 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
12846 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
12848 e = (e << mLen) | m
12850 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
12852 buffer[offset + i - d] |= s * 128
12855 },{}],16:[function(require,module,exports){
12856 var toString = {}.toString;
12858 module.exports = Array.isArray || function (arr) {
12859 return toString.call(arr) == '[object Array]';
12862 },{}],17:[function(require,module,exports){
12863 (function (global){
12865 * marked - a markdown parser
12866 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
12867 * https://github.com/chjj/marked
12873 * Block-Level Grammar
12878 code: /^( {4}[^\n]+\n*)+/,
12880 hr: /^( *[-*_]){3,} *(?:\n+|$)/,
12881 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
12883 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
12884 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
12885 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
12886 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
12887 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
12889 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
12893 block.bullet = /(?:[*+-]|\d+\.)/;
12894 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
12895 block.item = replace(block.item, 'gm')
12896 (/bull/g, block.bullet)
12899 block.list = replace(block.list)
12900 (/bull/g, block.bullet)
12901 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
12902 ('def', '\\n+(?=' + block.def.source + ')')
12905 block.blockquote = replace(block.blockquote)
12909 block._tag = '(?!(?:'
12910 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
12911 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
12912 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
12914 block.html = replace(block.html)
12915 ('comment', /<!--[\s\S]*?-->/)
12916 ('closed', /<(tag)[\s\S]+?<\/\1>/)
12917 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
12918 (/tag/g, block._tag)
12921 block.paragraph = replace(block.paragraph)
12923 ('heading', block.heading)
12924 ('lheading', block.lheading)
12925 ('blockquote', block.blockquote)
12926 ('tag', '<' + block._tag)
12931 * Normal Block Grammar
12934 block.normal = merge({}, block);
12937 * GFM Block Grammar
12940 block.gfm = merge({}, block.normal, {
12941 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
12943 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
12946 block.gfm.paragraph = replace(block.paragraph)
12948 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
12949 + block.list.source.replace('\\1', '\\3') + '|')
12953 * GFM + Tables Block Grammar
12956 block.tables = merge({}, block.gfm, {
12957 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
12958 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
12965 function Lexer(options) {
12967 this.tokens.links = {};
12968 this.options = options || marked.defaults;
12969 this.rules = block.normal;
12971 if (this.options.gfm) {
12972 if (this.options.tables) {
12973 this.rules = block.tables;
12975 this.rules = block.gfm;
12981 * Expose Block Rules
12984 Lexer.rules = block;
12987 * Static Lex Method
12990 Lexer.lex = function(src, options) {
12991 var lexer = new Lexer(options);
12992 return lexer.lex(src);
12999 Lexer.prototype.lex = function(src) {
13001 .replace(/\r\n|\r/g, '\n')
13002 .replace(/\t/g, ' ')
13003 .replace(/\u00a0/g, ' ')
13004 .replace(/\u2424/g, '\n');
13006 return this.token(src, true);
13013 Lexer.prototype.token = function(src, top, bq) {
13014 var src = src.replace(/^ +$/gm, '')
13027 if (cap = this.rules.newline.exec(src)) {
13028 src = src.substring(cap[0].length);
13029 if (cap[0].length > 1) {
13037 if (cap = this.rules.code.exec(src)) {
13038 src = src.substring(cap[0].length);
13039 cap = cap[0].replace(/^ {4}/gm, '');
13042 text: !this.options.pedantic
13043 ? cap.replace(/\n+$/, '')
13050 if (cap = this.rules.fences.exec(src)) {
13051 src = src.substring(cap[0].length);
13061 if (cap = this.rules.heading.exec(src)) {
13062 src = src.substring(cap[0].length);
13065 depth: cap[1].length,
13071 // table no leading pipe (gfm)
13072 if (top && (cap = this.rules.nptable.exec(src))) {
13073 src = src.substring(cap[0].length);
13077 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
13078 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
13079 cells: cap[3].replace(/\n$/, '').split('\n')
13082 for (i = 0; i < item.align.length; i++) {
13083 if (/^ *-+: *$/.test(item.align[i])) {
13084 item.align[i] = 'right';
13085 } else if (/^ *:-+: *$/.test(item.align[i])) {
13086 item.align[i] = 'center';
13087 } else if (/^ *:-+ *$/.test(item.align[i])) {
13088 item.align[i] = 'left';
13090 item.align[i] = null;
13094 for (i = 0; i < item.cells.length; i++) {
13095 item.cells[i] = item.cells[i].split(/ *\| */);
13098 this.tokens.push(item);
13104 if (cap = this.rules.lheading.exec(src)) {
13105 src = src.substring(cap[0].length);
13108 depth: cap[2] === '=' ? 1 : 2,
13115 if (cap = this.rules.hr.exec(src)) {
13116 src = src.substring(cap[0].length);
13124 if (cap = this.rules.blockquote.exec(src)) {
13125 src = src.substring(cap[0].length);
13128 type: 'blockquote_start'
13131 cap = cap[0].replace(/^ *> ?/gm, '');
13133 // Pass `top` to keep the current
13134 // "toplevel" state. This is exactly
13135 // how markdown.pl works.
13136 this.token(cap, top, true);
13139 type: 'blockquote_end'
13146 if (cap = this.rules.list.exec(src)) {
13147 src = src.substring(cap[0].length);
13151 type: 'list_start',
13152 ordered: bull.length > 1
13155 // Get each top-level item.
13156 cap = cap[0].match(this.rules.item);
13162 for (; i < l; i++) {
13165 // Remove the list item's bullet
13166 // so it is seen as the next token.
13167 space = item.length;
13168 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
13170 // Outdent whatever the
13171 // list item contains. Hacky.
13172 if (~item.indexOf('\n ')) {
13173 space -= item.length;
13174 item = !this.options.pedantic
13175 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
13176 : item.replace(/^ {1,4}/gm, '');
13179 // Determine whether the next list item belongs here.
13180 // Backpedal if it does not belong in this list.
13181 if (this.options.smartLists && i !== l - 1) {
13182 b = block.bullet.exec(cap[i + 1])[0];
13183 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
13184 src = cap.slice(i + 1).join('\n') + src;
13189 // Determine whether item is loose or not.
13190 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
13191 // for discount behavior.
13192 loose = next || /\n\n(?!\s*$)/.test(item);
13194 next = item.charAt(item.length - 1) === '\n';
13195 if (!loose) loose = next;
13200 ? 'loose_item_start'
13201 : 'list_item_start'
13205 this.token(item, false, bq);
13208 type: 'list_item_end'
13220 if (cap = this.rules.html.exec(src)) {
13221 src = src.substring(cap[0].length);
13223 type: this.options.sanitize
13226 pre: !this.options.sanitizer
13227 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
13234 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
13235 src = src.substring(cap[0].length);
13236 this.tokens.links[cap[1].toLowerCase()] = {
13244 if (top && (cap = this.rules.table.exec(src))) {
13245 src = src.substring(cap[0].length);
13249 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
13250 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
13251 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
13254 for (i = 0; i < item.align.length; i++) {
13255 if (/^ *-+: *$/.test(item.align[i])) {
13256 item.align[i] = 'right';
13257 } else if (/^ *:-+: *$/.test(item.align[i])) {
13258 item.align[i] = 'center';
13259 } else if (/^ *:-+ *$/.test(item.align[i])) {
13260 item.align[i] = 'left';
13262 item.align[i] = null;
13266 for (i = 0; i < item.cells.length; i++) {
13267 item.cells[i] = item.cells[i]
13268 .replace(/^ *\| *| *\| *$/g, '')
13272 this.tokens.push(item);
13277 // top-level paragraph
13278 if (top && (cap = this.rules.paragraph.exec(src))) {
13279 src = src.substring(cap[0].length);
13282 text: cap[1].charAt(cap[1].length - 1) === '\n'
13283 ? cap[1].slice(0, -1)
13290 if (cap = this.rules.text.exec(src)) {
13291 // Top-level should never reach here.
13292 src = src.substring(cap[0].length);
13302 Error('Infinite loop on byte: ' + src.charCodeAt(0));
13306 return this.tokens;
13310 * Inline-Level Grammar
13314 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
13315 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
13317 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
13318 link: /^!?\[(inside)\]\(href\)/,
13319 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
13320 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
13321 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
13322 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
13323 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
13324 br: /^ {2,}\n(?!\s*$)/,
13326 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
13329 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
13330 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
13332 inline.link = replace(inline.link)
13333 ('inside', inline._inside)
13334 ('href', inline._href)
13337 inline.reflink = replace(inline.reflink)
13338 ('inside', inline._inside)
13342 * Normal Inline Grammar
13345 inline.normal = merge({}, inline);
13348 * Pedantic Inline Grammar
13351 inline.pedantic = merge({}, inline.normal, {
13352 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
13353 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
13357 * GFM Inline Grammar
13360 inline.gfm = merge({}, inline.normal, {
13361 escape: replace(inline.escape)('])', '~|])')(),
13362 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
13363 del: /^~~(?=\S)([\s\S]*?\S)~~/,
13364 text: replace(inline.text)
13366 ('|', '|https?://|')
13371 * GFM + Line Breaks Inline Grammar
13374 inline.breaks = merge({}, inline.gfm, {
13375 br: replace(inline.br)('{2,}', '*')(),
13376 text: replace(inline.gfm.text)('{2,}', '*')()
13380 * Inline Lexer & Compiler
13383 function InlineLexer(links, options) {
13384 this.options = options || marked.defaults;
13385 this.links = links;
13386 this.rules = inline.normal;
13387 this.renderer = this.options.renderer || new Renderer;
13388 this.renderer.options = this.options;
13392 Error('Tokens array requires a `links` property.');
13395 if (this.options.gfm) {
13396 if (this.options.breaks) {
13397 this.rules = inline.breaks;
13399 this.rules = inline.gfm;
13401 } else if (this.options.pedantic) {
13402 this.rules = inline.pedantic;
13407 * Expose Inline Rules
13410 InlineLexer.rules = inline;
13413 * Static Lexing/Compiling Method
13416 InlineLexer.output = function(src, links, options) {
13417 var inline = new InlineLexer(links, options);
13418 return inline.output(src);
13425 InlineLexer.prototype.output = function(src) {
13434 if (cap = this.rules.escape.exec(src)) {
13435 src = src.substring(cap[0].length);
13441 if (cap = this.rules.autolink.exec(src)) {
13442 src = src.substring(cap[0].length);
13443 if (cap[2] === '@') {
13444 text = cap[1].charAt(6) === ':'
13445 ? this.mangle(cap[1].substring(7))
13446 : this.mangle(cap[1]);
13447 href = this.mangle('mailto:') + text;
13449 text = escape(cap[1]);
13452 out += this.renderer.link(href, null, text);
13457 if (!this.inLink && (cap = this.rules.url.exec(src))) {
13458 src = src.substring(cap[0].length);
13459 text = escape(cap[1]);
13461 out += this.renderer.link(href, null, text);
13466 if (cap = this.rules.tag.exec(src)) {
13467 if (!this.inLink && /^<a /i.test(cap[0])) {
13468 this.inLink = true;
13469 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
13470 this.inLink = false;
13472 src = src.substring(cap[0].length);
13473 out += this.options.sanitize
13474 ? this.options.sanitizer
13475 ? this.options.sanitizer(cap[0])
13482 if (cap = this.rules.link.exec(src)) {
13483 src = src.substring(cap[0].length);
13484 this.inLink = true;
13485 out += this.outputLink(cap, {
13489 this.inLink = false;
13494 if ((cap = this.rules.reflink.exec(src))
13495 || (cap = this.rules.nolink.exec(src))) {
13496 src = src.substring(cap[0].length);
13497 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
13498 link = this.links[link.toLowerCase()];
13499 if (!link || !link.href) {
13500 out += cap[0].charAt(0);
13501 src = cap[0].substring(1) + src;
13504 this.inLink = true;
13505 out += this.outputLink(cap, link);
13506 this.inLink = false;
13511 if (cap = this.rules.strong.exec(src)) {
13512 src = src.substring(cap[0].length);
13513 out += this.renderer.strong(this.output(cap[2] || cap[1]));
13518 if (cap = this.rules.em.exec(src)) {
13519 src = src.substring(cap[0].length);
13520 out += this.renderer.em(this.output(cap[2] || cap[1]));
13525 if (cap = this.rules.code.exec(src)) {
13526 src = src.substring(cap[0].length);
13527 out += this.renderer.codespan(escape(cap[2], true));
13532 if (cap = this.rules.br.exec(src)) {
13533 src = src.substring(cap[0].length);
13534 out += this.renderer.br();
13539 if (cap = this.rules.del.exec(src)) {
13540 src = src.substring(cap[0].length);
13541 out += this.renderer.del(this.output(cap[1]));
13546 if (cap = this.rules.text.exec(src)) {
13547 src = src.substring(cap[0].length);
13548 out += this.renderer.text(escape(this.smartypants(cap[0])));
13554 Error('Infinite loop on byte: ' + src.charCodeAt(0));
13565 InlineLexer.prototype.outputLink = function(cap, link) {
13566 var href = escape(link.href)
13567 , title = link.title ? escape(link.title) : null;
13569 return cap[0].charAt(0) !== '!'
13570 ? this.renderer.link(href, title, this.output(cap[1]))
13571 : this.renderer.image(href, title, escape(cap[1]));
13575 * Smartypants Transformations
13578 InlineLexer.prototype.smartypants = function(text) {
13579 if (!this.options.smartypants) return text;
13582 .replace(/---/g, '\u2014')
13584 .replace(/--/g, '\u2013')
13586 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
13587 // closing singles & apostrophes
13588 .replace(/'/g, '\u2019')
13590 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
13592 .replace(/"/g, '\u201d')
13594 .replace(/\.{3}/g, '\u2026');
13601 InlineLexer.prototype.mangle = function(text) {
13602 if (!this.options.mangle) return text;
13608 for (; i < l; i++) {
13609 ch = text.charCodeAt(i);
13610 if (Math.random() > 0.5) {
13611 ch = 'x' + ch.toString(16);
13613 out += '&#' + ch + ';';
13623 function Renderer(options) {
13624 this.options = options || {};
13627 Renderer.prototype.code = function(code, lang, escaped) {
13628 if (this.options.highlight) {
13629 var out = this.options.highlight(code, lang);
13630 if (out != null && out !== code) {
13637 return '<pre><code>'
13638 + (escaped ? code : escape(code, true))
13639 + '\n</code></pre>';
13642 return '<pre><code class="'
13643 + this.options.langPrefix
13644 + escape(lang, true)
13646 + (escaped ? code : escape(code, true))
13647 + '\n</code></pre>\n';
13650 Renderer.prototype.blockquote = function(quote) {
13651 return '<blockquote>\n' + quote + '</blockquote>\n';
13654 Renderer.prototype.html = function(html) {
13658 Renderer.prototype.heading = function(text, level, raw) {
13662 + this.options.headerPrefix
13663 + raw.toLowerCase().replace(/[^\w]+/g, '-')
13671 Renderer.prototype.hr = function() {
13672 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
13675 Renderer.prototype.list = function(body, ordered) {
13676 var type = ordered ? 'ol' : 'ul';
13677 return '<' + type + '>\n' + body + '</' + type + '>\n';
13680 Renderer.prototype.listitem = function(text) {
13681 return '<li>' + text + '</li>\n';
13684 Renderer.prototype.paragraph = function(text) {
13685 return '<p>' + text + '</p>\n';
13688 Renderer.prototype.table = function(header, body) {
13699 Renderer.prototype.tablerow = function(content) {
13700 return '<tr>\n' + content + '</tr>\n';
13703 Renderer.prototype.tablecell = function(content, flags) {
13704 var type = flags.header ? 'th' : 'td';
13705 var tag = flags.align
13706 ? '<' + type + ' style="text-align:' + flags.align + '">'
13707 : '<' + type + '>';
13708 return tag + content + '</' + type + '>\n';
13711 // span level renderer
13712 Renderer.prototype.strong = function(text) {
13713 return '<strong>' + text + '</strong>';
13716 Renderer.prototype.em = function(text) {
13717 return '<em>' + text + '</em>';
13720 Renderer.prototype.codespan = function(text) {
13721 return '<code>' + text + '</code>';
13724 Renderer.prototype.br = function() {
13725 return this.options.xhtml ? '<br/>' : '<br>';
13728 Renderer.prototype.del = function(text) {
13729 return '<del>' + text + '</del>';
13732 Renderer.prototype.link = function(href, title, text) {
13733 if (this.options.sanitize) {
13735 var prot = decodeURIComponent(unescape(href))
13736 .replace(/[^\w:]/g, '')
13741 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
13745 var out = '<a href="' + href + '"';
13747 out += ' title="' + title + '"';
13749 out += '>' + text + '</a>';
13753 Renderer.prototype.image = function(href, title, text) {
13754 var out = '<img src="' + href + '" alt="' + text + '"';
13756 out += ' title="' + title + '"';
13758 out += this.options.xhtml ? '/>' : '>';
13762 Renderer.prototype.text = function(text) {
13767 * Parsing & Compiling
13770 function Parser(options) {
13773 this.options = options || marked.defaults;
13774 this.options.renderer = this.options.renderer || new Renderer;
13775 this.renderer = this.options.renderer;
13776 this.renderer.options = this.options;
13780 * Static Parse Method
13783 Parser.parse = function(src, options, renderer) {
13784 var parser = new Parser(options, renderer);
13785 return parser.parse(src);
13792 Parser.prototype.parse = function(src) {
13793 this.inline = new InlineLexer(src.links, this.options, this.renderer);
13794 this.tokens = src.reverse();
13797 while (this.next()) {
13808 Parser.prototype.next = function() {
13809 return this.token = this.tokens.pop();
13813 * Preview Next Token
13816 Parser.prototype.peek = function() {
13817 return this.tokens[this.tokens.length - 1] || 0;
13821 * Parse Text Tokens
13824 Parser.prototype.parseText = function() {
13825 var body = this.token.text;
13827 while (this.peek().type === 'text') {
13828 body += '\n' + this.next().text;
13831 return this.inline.output(body);
13835 * Parse Current Token
13838 Parser.prototype.tok = function() {
13839 switch (this.token.type) {
13844 return this.renderer.hr();
13847 return this.renderer.heading(
13848 this.inline.output(this.token.text),
13853 return this.renderer.code(this.token.text,
13855 this.token.escaped);
13868 for (i = 0; i < this.token.header.length; i++) {
13869 flags = { header: true, align: this.token.align[i] };
13870 cell += this.renderer.tablecell(
13871 this.inline.output(this.token.header[i]),
13872 { header: true, align: this.token.align[i] }
13875 header += this.renderer.tablerow(cell);
13877 for (i = 0; i < this.token.cells.length; i++) {
13878 row = this.token.cells[i];
13881 for (j = 0; j < row.length; j++) {
13882 cell += this.renderer.tablecell(
13883 this.inline.output(row[j]),
13884 { header: false, align: this.token.align[j] }
13888 body += this.renderer.tablerow(cell);
13890 return this.renderer.table(header, body);
13892 case 'blockquote_start': {
13895 while (this.next().type !== 'blockquote_end') {
13896 body += this.tok();
13899 return this.renderer.blockquote(body);
13901 case 'list_start': {
13903 , ordered = this.token.ordered;
13905 while (this.next().type !== 'list_end') {
13906 body += this.tok();
13909 return this.renderer.list(body, ordered);
13911 case 'list_item_start': {
13914 while (this.next().type !== 'list_item_end') {
13915 body += this.token.type === 'text'
13920 return this.renderer.listitem(body);
13922 case 'loose_item_start': {
13925 while (this.next().type !== 'list_item_end') {
13926 body += this.tok();
13929 return this.renderer.listitem(body);
13932 var html = !this.token.pre && !this.options.pedantic
13933 ? this.inline.output(this.token.text)
13935 return this.renderer.html(html);
13937 case 'paragraph': {
13938 return this.renderer.paragraph(this.inline.output(this.token.text));
13941 return this.renderer.paragraph(this.parseText());
13950 function escape(html, encode) {
13952 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
13953 .replace(/</g, '<')
13954 .replace(/>/g, '>')
13955 .replace(/"/g, '"')
13956 .replace(/'/g, ''');
13959 function unescape(html) {
13960 return html.replace(/&([#\w]+);/g, function(_, n) {
13961 n = n.toLowerCase();
13962 if (n === 'colon') return ':';
13963 if (n.charAt(0) === '#') {
13964 return n.charAt(1) === 'x'
13965 ? String.fromCharCode(parseInt(n.substring(2), 16))
13966 : String.fromCharCode(+n.substring(1));
13972 function replace(regex, opt) {
13973 regex = regex.source;
13975 return function self(name, val) {
13976 if (!name) return new RegExp(regex, opt);
13977 val = val.source || val;
13978 val = val.replace(/(^|[^\[])\^/g, '$1');
13979 regex = regex.replace(name, val);
13987 function merge(obj) {
13992 for (; i < arguments.length; i++) {
13993 target = arguments[i];
13994 for (key in target) {
13995 if (Object.prototype.hasOwnProperty.call(target, key)) {
13996 obj[key] = target[key];
14009 function marked(src, opt, callback) {
14010 if (callback || typeof opt === 'function') {
14016 opt = merge({}, marked.defaults, opt || {});
14018 var highlight = opt.highlight
14024 tokens = Lexer.lex(src, opt)
14026 return callback(e);
14029 pending = tokens.length;
14031 var done = function(err) {
14033 opt.highlight = highlight;
14034 return callback(err);
14040 out = Parser.parse(tokens, opt);
14045 opt.highlight = highlight;
14049 : callback(null, out);
14052 if (!highlight || highlight.length < 3) {
14056 delete opt.highlight;
14058 if (!pending) return done();
14060 for (; i < tokens.length; i++) {
14062 if (token.type !== 'code') {
14063 return --pending || done();
14065 return highlight(token.text, token.lang, function(err, code) {
14066 if (err) return done(err);
14067 if (code == null || code === token.text) {
14068 return --pending || done();
14071 token.escaped = true;
14072 --pending || done();
14080 if (opt) opt = merge({}, marked.defaults, opt);
14081 return Parser.parse(Lexer.lex(src, opt), opt);
14083 e.message += '\nPlease report this to https://github.com/chjj/marked.';
14084 if ((opt || marked.defaults).silent) {
14085 return '<p>An error occured:</p><pre>'
14086 + escape(e.message + '', true)
14098 marked.setOptions = function(opt) {
14099 merge(marked.defaults, opt);
14103 marked.defaults = {
14114 langPrefix: 'lang-',
14115 smartypants: false,
14117 renderer: new Renderer,
14125 marked.Parser = Parser;
14126 marked.parser = Parser.parse;
14128 marked.Renderer = Renderer;
14130 marked.Lexer = Lexer;
14131 marked.lexer = Lexer.lex;
14133 marked.InlineLexer = InlineLexer;
14134 marked.inlineLexer = InlineLexer.output;
14136 marked.parse = marked;
14138 if (typeof module !== 'undefined' && typeof exports === 'object') {
14139 module.exports = marked;
14140 } else if (typeof define === 'function' && define.amd) {
14141 define(function() { return marked; });
14143 this.marked = marked;
14146 }).call(function() {
14147 return this || (typeof window !== 'undefined' ? window : global);
14150 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
14151 },{}],18:[function(require,module,exports){
14152 (function (Buffer,__dirname){
14156 * Typo is a JavaScript implementation of a spellchecker using hunspell-style
14161 * Typo constructor.
14163 * @param {String} [dictionary] The locale code of the dictionary being used. e.g.,
14164 * "en_US". This is only used to auto-load dictionaries.
14165 * @param {String} [affData] The data from the dictionary's .aff file. If omitted
14166 * and Typo.js is being used in a Chrome extension, the .aff
14167 * file will be loaded automatically from
14168 * lib/typo/dictionaries/[dictionary]/[dictionary].aff
14169 * In other environments, it will be loaded from
14170 * [settings.dictionaryPath]/dictionaries/[dictionary]/[dictionary].aff
14171 * @param {String} [wordsData] The data from the dictionary's .dic file. If omitted
14172 * and Typo.js is being used in a Chrome extension, the .dic
14173 * file will be loaded automatically from
14174 * lib/typo/dictionaries/[dictionary]/[dictionary].dic
14175 * In other environments, it will be loaded from
14176 * [settings.dictionaryPath]/dictionaries/[dictionary]/[dictionary].dic
14177 * @param {Object} [settings] Constructor settings. Available properties are:
14178 * {String} [dictionaryPath]: path to load dictionary from in non-chrome
14180 * {Object} [flags]: flag information.
14183 * @returns {Typo} A Typo object.
14186 var Typo = function (dictionary, affData, wordsData, settings) {
14187 settings = settings || {};
14189 this.dictionary = null;
14192 this.dictionaryTable = {};
14194 this.compoundRules = [];
14195 this.compoundRuleCodes = {};
14197 this.replacementTable = [];
14199 this.flags = settings.flags || {};
14202 this.dictionary = dictionary;
14204 if (typeof window !== 'undefined' && 'chrome' in window && 'extension' in window.chrome && 'getURL' in window.chrome.extension) {
14205 if (!affData) affData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".aff"));
14206 if (!wordsData) wordsData = this._readFile(chrome.extension.getURL("lib/typo/dictionaries/" + dictionary + "/" + dictionary + ".dic"));
14208 if (settings.dictionaryPath) {
14209 var path = settings.dictionaryPath;
14211 else if (typeof __dirname !== 'undefined') {
14212 var path = __dirname + '/dictionaries';
14215 var path = './dictionaries';
14218 if (!affData) affData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".aff");
14219 if (!wordsData) wordsData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".dic");
14222 this.rules = this._parseAFF(affData);
14224 // Save the rule codes that are used in compound rules.
14225 this.compoundRuleCodes = {};
14227 for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
14228 var rule = this.compoundRules[i];
14230 for (var j = 0, _jlen = rule.length; j < _jlen; j++) {
14231 this.compoundRuleCodes[rule[j]] = [];
14235 // If we add this ONLYINCOMPOUND flag to this.compoundRuleCodes, then _parseDIC
14236 // will do the work of saving the list of words that are compound-only.
14237 if ("ONLYINCOMPOUND" in this.flags) {
14238 this.compoundRuleCodes[this.flags.ONLYINCOMPOUND] = [];
14241 this.dictionaryTable = this._parseDIC(wordsData);
14243 // Get rid of any codes from the compound rule codes that are never used
14244 // (or that were special regex characters). Not especially necessary...
14245 for (var i in this.compoundRuleCodes) {
14246 if (this.compoundRuleCodes[i].length == 0) {
14247 delete this.compoundRuleCodes[i];
14251 // Build the full regular expressions for each compound rule.
14252 // I have a feeling (but no confirmation yet) that this method of
14253 // testing for compound words is probably slow.
14254 for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
14255 var ruleText = this.compoundRules[i];
14257 var expressionText = "";
14259 for (var j = 0, _jlen = ruleText.length; j < _jlen; j++) {
14260 var character = ruleText[j];
14262 if (character in this.compoundRuleCodes) {
14263 expressionText += "(" + this.compoundRuleCodes[character].join("|") + ")";
14266 expressionText += character;
14270 this.compoundRules[i] = new RegExp(expressionText, "i");
14279 * Loads a Typo instance from a hash of all of the Typo properties.
14281 * @param object obj A hash of Typo properties, probably gotten from a JSON.parse(JSON.stringify(typo_instance)).
14284 load : function (obj) {
14285 for (var i in obj) {
14293 * Read the contents of a file.
14295 * @param {String} path The path (relative) to the file.
14296 * @param {String} [charset="ISO8859-1"] The expected charset of the file
14297 * @returns string The file data.
14300 _readFile : function (path, charset) {
14301 if (!charset) charset = "utf8";
14303 if (typeof XMLHttpRequest !== 'undefined') {
14304 var req = new XMLHttpRequest();
14305 req.open("GET", path, false);
14307 if (req.overrideMimeType)
14308 req.overrideMimeType("text/plain; charset=" + charset);
14312 return req.responseText;
14314 else if (typeof require !== 'undefined') {
14316 var fs = require("fs");
14319 if (fs.existsSync(path)) {
14320 var stats = fs.statSync(path);
14322 var fileDescriptor = fs.openSync(path, 'r');
14324 var buffer = new Buffer(stats.size);
14326 fs.readSync(fileDescriptor, buffer, 0, buffer.length, null);
14328 return buffer.toString(charset, 0, buffer.length);
14331 console.log("Path " + path + " does not exist.");
14341 * Parse the rules out from a .aff file.
14343 * @param {String} data The contents of the affix file.
14344 * @returns object The rules from the file.
14347 _parseAFF : function (data) {
14350 // Remove comment lines
14351 data = this._removeAffixComments(data);
14353 var lines = data.split("\n");
14355 for (var i = 0, _len = lines.length; i < _len; i++) {
14356 var line = lines[i];
14358 var definitionParts = line.split(/\s+/);
14360 var ruleType = definitionParts[0];
14362 if (ruleType == "PFX" || ruleType == "SFX") {
14363 var ruleCode = definitionParts[1];
14364 var combineable = definitionParts[2];
14365 var numEntries = parseInt(definitionParts[3], 10);
14369 for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
14370 var line = lines[j];
14372 var lineParts = line.split(/\s+/);
14373 var charactersToRemove = lineParts[2];
14375 var additionParts = lineParts[3].split("/");
14377 var charactersToAdd = additionParts[0];
14378 if (charactersToAdd === "0") charactersToAdd = "";
14380 var continuationClasses = this.parseRuleCodes(additionParts[1]);
14382 var regexToMatch = lineParts[4];
14385 entry.add = charactersToAdd;
14387 if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses;
14389 if (regexToMatch !== ".") {
14390 if (ruleType === "SFX") {
14391 entry.match = new RegExp(regexToMatch + "$");
14394 entry.match = new RegExp("^" + regexToMatch);
14398 if (charactersToRemove != "0") {
14399 if (ruleType === "SFX") {
14400 entry.remove = new RegExp(charactersToRemove + "$");
14403 entry.remove = charactersToRemove;
14407 entries.push(entry);
14410 rules[ruleCode] = { "type" : ruleType, "combineable" : (combineable == "Y"), "entries" : entries };
14414 else if (ruleType === "COMPOUNDRULE") {
14415 var numEntries = parseInt(definitionParts[1], 10);
14417 for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) {
14418 var line = lines[j];
14420 var lineParts = line.split(/\s+/);
14421 this.compoundRules.push(lineParts[1]);
14426 else if (ruleType === "REP") {
14427 var lineParts = line.split(/\s+/);
14429 if (lineParts.length === 3) {
14430 this.replacementTable.push([ lineParts[1], lineParts[2] ]);
14440 this.flags[ruleType] = definitionParts[1];
14448 * Removes comment lines and then cleans up blank lines and trailing whitespace.
14450 * @param {String} data The data from an affix file.
14451 * @return {String} The cleaned-up data.
14454 _removeAffixComments : function (data) {
14456 data = data.replace(/#.*$/mg, "");
14459 data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, '');
14461 // Remove blank lines.
14462 data = data.replace(/\n{2,}/g, "\n");
14464 // Trim the entire string
14465 data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
14471 * Parses the words out from the .dic file.
14473 * @param {String} data The data from the dictionary file.
14474 * @returns object The lookup table containing all of the words and
14475 * word forms from the dictionary.
14478 _parseDIC : function (data) {
14479 data = this._removeDicComments(data);
14481 var lines = data.split("\n");
14482 var dictionaryTable = {};
14484 function addWord(word, rules) {
14485 // Some dictionaries will list the same word multiple times with different rule sets.
14486 if (!(word in dictionaryTable) || typeof dictionaryTable[word] != 'object') {
14487 dictionaryTable[word] = [];
14490 dictionaryTable[word].push(rules);
14493 // The first line is the number of words in the dictionary.
14494 for (var i = 1, _len = lines.length; i < _len; i++) {
14495 var line = lines[i];
14497 var parts = line.split("/", 2);
14499 var word = parts[0];
14501 // Now for each affix rule, generate that form of the word.
14502 if (parts.length > 1) {
14503 var ruleCodesArray = this.parseRuleCodes(parts[1]);
14505 // Save the ruleCodes for compound word situations.
14506 if (!("NEEDAFFIX" in this.flags) || ruleCodesArray.indexOf(this.flags.NEEDAFFIX) == -1) {
14507 addWord(word, ruleCodesArray);
14510 for (var j = 0, _jlen = ruleCodesArray.length; j < _jlen; j++) {
14511 var code = ruleCodesArray[j];
14513 var rule = this.rules[code];
14516 var newWords = this._applyRule(word, rule);
14518 for (var ii = 0, _iilen = newWords.length; ii < _iilen; ii++) {
14519 var newWord = newWords[ii];
14521 addWord(newWord, []);
14523 if (rule.combineable) {
14524 for (var k = j + 1; k < _jlen; k++) {
14525 var combineCode = ruleCodesArray[k];
14527 var combineRule = this.rules[combineCode];
14530 if (combineRule.combineable && (rule.type != combineRule.type)) {
14531 var otherNewWords = this._applyRule(newWord, combineRule);
14533 for (var iii = 0, _iiilen = otherNewWords.length; iii < _iiilen; iii++) {
14534 var otherNewWord = otherNewWords[iii];
14535 addWord(otherNewWord, []);
14544 if (code in this.compoundRuleCodes) {
14545 this.compoundRuleCodes[code].push(word);
14550 addWord(word.trim(), []);
14554 return dictionaryTable;
14559 * Removes comment lines and then cleans up blank lines and trailing whitespace.
14561 * @param {String} data The data from a .dic file.
14562 * @return {String} The cleaned-up data.
14565 _removeDicComments : function (data) {
14566 // I can't find any official documentation on it, but at least the de_DE
14567 // dictionary uses tab-indented lines as comments.
14570 data = data.replace(/^\t.*$/mg, "");
14575 parseRuleCodes : function (textCodes) {
14579 else if (!("FLAG" in this.flags)) {
14580 return textCodes.split("");
14582 else if (this.flags.FLAG === "long") {
14585 for (var i = 0, _len = textCodes.length; i < _len; i += 2) {
14586 flags.push(textCodes.substr(i, 2));
14591 else if (this.flags.FLAG === "num") {
14592 return textCode.split(",");
14597 * Applies an affix rule to a word.
14599 * @param {String} word The base word.
14600 * @param {Object} rule The affix rule.
14601 * @returns {String[]} The new words generated by the rule.
14604 _applyRule : function (word, rule) {
14605 var entries = rule.entries;
14608 for (var i = 0, _len = entries.length; i < _len; i++) {
14609 var entry = entries[i];
14611 if (!entry.match || word.match(entry.match)) {
14612 var newWord = word;
14614 if (entry.remove) {
14615 newWord = newWord.replace(entry.remove, "");
14618 if (rule.type === "SFX") {
14619 newWord = newWord + entry.add;
14622 newWord = entry.add + newWord;
14625 newWords.push(newWord);
14627 if ("continuationClasses" in entry) {
14628 for (var j = 0, _jlen = entry.continuationClasses.length; j < _jlen; j++) {
14629 var continuationRule = this.rules[entry.continuationClasses[j]];
14631 if (continuationRule) {
14632 newWords = newWords.concat(this._applyRule(newWord, continuationRule));
14636 // This shouldn't happen, but it does, at least in the de_DE dictionary.
14637 // I think the author mistakenly supplied lower-case rule codes instead
14650 * Checks whether a word or a capitalization variant exists in the current dictionary.
14651 * The word is trimmed and several variations of capitalizations are checked.
14652 * If you want to check a word without any changes made to it, call checkExact()
14654 * @see http://blog.stevenlevithan.com/archives/faster-trim-javascript re:trimming function
14656 * @param {String} aWord The word to check.
14657 * @returns {Boolean}
14660 check : function (aWord) {
14661 // Remove leading and trailing whitespace
14662 var trimmedWord = aWord.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
14664 if (this.checkExact(trimmedWord)) {
14668 // The exact word is not in the dictionary.
14669 if (trimmedWord.toUpperCase() === trimmedWord) {
14670 // The word was supplied in all uppercase.
14671 // Check for a capitalized form of the word.
14672 var capitalizedWord = trimmedWord[0] + trimmedWord.substring(1).toLowerCase();
14674 if (this.hasFlag(capitalizedWord, "KEEPCASE")) {
14675 // Capitalization variants are not allowed for this word.
14679 if (this.checkExact(capitalizedWord)) {
14684 var lowercaseWord = trimmedWord.toLowerCase();
14686 if (lowercaseWord !== trimmedWord) {
14687 if (this.hasFlag(lowercaseWord, "KEEPCASE")) {
14688 // Capitalization variants are not allowed for this word.
14692 // Check for a lowercase form
14693 if (this.checkExact(lowercaseWord)) {
14702 * Checks whether a word exists in the current dictionary.
14704 * @param {String} word The word to check.
14705 * @returns {Boolean}
14708 checkExact : function (word) {
14709 var ruleCodes = this.dictionaryTable[word];
14711 if (typeof ruleCodes === 'undefined') {
14712 // Check if this might be a compound word.
14713 if ("COMPOUNDMIN" in this.flags && word.length >= this.flags.COMPOUNDMIN) {
14714 for (var i = 0, _len = this.compoundRules.length; i < _len; i++) {
14715 if (word.match(this.compoundRules[i])) {
14723 else if (typeof ruleCodes === 'object') { // this.dictionary['hasOwnProperty'] will be a function.
14724 for (var i = 0, _len = ruleCodes.length; i < _len; i++) {
14725 if (!this.hasFlag(word, "ONLYINCOMPOUND", ruleCodes[i])) {
14735 * Looks up whether a given word is flagged with a given flag.
14737 * @param {String} word The word in question.
14738 * @param {String} flag The flag in question.
14739 * @return {Boolean}
14742 hasFlag : function (word, flag, wordFlags) {
14743 if (flag in this.flags) {
14744 if (typeof wordFlags === 'undefined') {
14745 var wordFlags = Array.prototype.concat.apply([], this.dictionaryTable[word]);
14748 if (wordFlags && wordFlags.indexOf(this.flags[flag]) !== -1) {
14757 * Returns a list of suggestions for a misspelled word.
14759 * @see http://www.norvig.com/spell-correct.html for the basis of this suggestor.
14760 * This suggestor is primitive, but it works.
14762 * @param {String} word The misspelling.
14763 * @param {Number} [limit=5] The maximum number of suggestions to return.
14764 * @returns {String[]} The array of suggestions.
14769 suggest : function (word, limit) {
14770 if (!limit) limit = 5;
14772 if (this.check(word)) return [];
14774 // Check the replacement table.
14775 for (var i = 0, _len = this.replacementTable.length; i < _len; i++) {
14776 var replacementEntry = this.replacementTable[i];
14778 if (word.indexOf(replacementEntry[0]) !== -1) {
14779 var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]);
14781 if (this.check(correctedWord)) {
14782 return [ correctedWord ];
14788 self.alphabet = "abcdefghijklmnopqrstuvwxyz";
14791 if (!self.alphabet) {
14792 // Use the alphabet as implicitly defined by the words in the dictionary.
14793 var alphaHash = {};
14795 for (var i in self.dictionaryTable) {
14796 for (var j = 0, _len = i.length; j < _len; j++) {
14797 alphaHash[i[j]] = true;
14801 for (var i in alphaHash) {
14802 self.alphabet += i;
14805 var alphaArray = self.alphabet.split("");
14807 self.alphabet = alphaArray.join("");
14811 function edits1(words) {
14814 for (var ii = 0, _iilen = words.length; ii < _iilen; ii++) {
14815 var word = words[ii];
14819 for (var i = 0, _len = word.length + 1; i < _len; i++) {
14820 splits.push([ word.substring(0, i), word.substring(i, word.length) ]);
14825 for (var i = 0, _len = splits.length; i < _len; i++) {
14829 deletes.push(s[0] + s[1].substring(1));
14833 var transposes = [];
14835 for (var i = 0, _len = splits.length; i < _len; i++) {
14838 if (s[1].length > 1) {
14839 transposes.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2));
14845 for (var i = 0, _len = splits.length; i < _len; i++) {
14849 for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
14850 replaces.push(s[0] + self.alphabet[j] + s[1].substring(1));
14857 for (var i = 0, _len = splits.length; i < _len; i++) {
14861 for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) {
14862 replaces.push(s[0] + self.alphabet[j] + s[1]);
14867 rv = rv.concat(deletes);
14868 rv = rv.concat(transposes);
14869 rv = rv.concat(replaces);
14870 rv = rv.concat(inserts);
14876 function known(words) {
14879 for (var i = 0; i < words.length; i++) {
14880 if (self.check(words[i])) {
14888 function correct(word) {
14889 // Get the edit-distance-1 and edit-distance-2 forms of this word.
14890 var ed1 = edits1([word]);
14891 var ed2 = edits1(ed1);
14893 var corrections = known(ed1).concat(known(ed2));
14895 // Sort the edits based on how many different ways they were created.
14896 var weighted_corrections = {};
14898 for (var i = 0, _len = corrections.length; i < _len; i++) {
14899 if (!(corrections[i] in weighted_corrections)) {
14900 weighted_corrections[corrections[i]] = 1;
14903 weighted_corrections[corrections[i]] += 1;
14907 var sorted_corrections = [];
14909 for (var i in weighted_corrections) {
14910 sorted_corrections.push([ i, weighted_corrections[i] ]);
14913 function sorter(a, b) {
14921 sorted_corrections.sort(sorter).reverse();
14925 for (var i = 0, _len = Math.min(limit, sorted_corrections.length); i < _len; i++) {
14926 if (!self.hasFlag(sorted_corrections[i][0], "NOSUGGEST")) {
14927 rv.push(sorted_corrections[i][0]);
14934 return correct(word);
14938 // Support for use as a node.js module.
14939 if (typeof module !== 'undefined') {
14940 module.exports = Typo;
14942 }).call(this,require("buffer").Buffer,"/node_modules/typo-js")
14943 },{"buffer":3,"fs":2}],19:[function(require,module,exports){
14944 // CodeMirror, copyright (c) by Marijn Haverbeke and others
14945 // Distributed under an MIT license: http://codemirror.net/LICENSE
14947 var CodeMirror = require("codemirror");
14949 CodeMirror.commands.tabAndIndentMarkdownList = function (cm) {
14950 var ranges = cm.listSelections();
14951 var pos = ranges[0].head;
14952 var eolState = cm.getStateAfter(pos.line);
14953 var inList = eolState.list !== false;
14956 cm.execCommand("indentMore");
14960 if (cm.options.indentWithTabs) {
14961 cm.execCommand("insertTab");
14964 var spaces = Array(cm.options.tabSize + 1).join(" ");
14965 cm.replaceSelection(spaces);
14969 CodeMirror.commands.shiftTabAndUnindentMarkdownList = function (cm) {
14970 var ranges = cm.listSelections();
14971 var pos = ranges[0].head;
14972 var eolState = cm.getStateAfter(pos.line);
14973 var inList = eolState.list !== false;
14976 cm.execCommand("indentLess");
14980 if (cm.options.indentWithTabs) {
14981 cm.execCommand("insertTab");
14984 var spaces = Array(cm.options.tabSize + 1).join(" ");
14985 cm.replaceSelection(spaces);
14989 },{"codemirror":10}],20:[function(require,module,exports){
14990 /*global require,module*/
14992 var CodeMirror = require("codemirror");
14993 require("codemirror/addon/edit/continuelist.js");
14994 require("./codemirror/tablist");
14995 require("codemirror/addon/display/fullscreen.js");
14996 require("codemirror/mode/markdown/markdown.js");
14997 require("codemirror/addon/mode/overlay.js");
14998 require("codemirror/addon/display/placeholder.js");
14999 require("codemirror/addon/selection/mark-selection.js");
15000 require("codemirror/mode/gfm/gfm.js");
15001 require("codemirror/mode/xml/xml.js");
15002 var CodeMirrorSpellChecker = require("codemirror-spell-checker");
15003 var marked = require("marked");
15007 var isMac = /Mac/.test(navigator.platform);
15009 // Mapping of actions that can be bound to keyboard shortcuts or toolbar buttons
15011 "toggleBold": toggleBold,
15012 "toggleItalic": toggleItalic,
15013 "drawLink": drawLink,
15014 "toggleHeadingSmaller": toggleHeadingSmaller,
15015 "toggleHeadingBigger": toggleHeadingBigger,
15016 "drawImage": drawImage,
15017 "toggleBlockquote": toggleBlockquote,
15018 "toggleOrderedList": toggleOrderedList,
15019 "toggleUnorderedList": toggleUnorderedList,
15020 "toggleCodeBlock": toggleCodeBlock,
15021 "togglePreview": togglePreview,
15022 "toggleStrikethrough": toggleStrikethrough,
15023 "toggleHeading1": toggleHeading1,
15024 "toggleHeading2": toggleHeading2,
15025 "toggleHeading3": toggleHeading3,
15026 "cleanBlock": cleanBlock,
15027 "drawTable": drawTable,
15028 "drawHorizontalRule": drawHorizontalRule,
15031 "toggleSideBySide": toggleSideBySide,
15032 "toggleFullScreen": toggleFullScreen
15036 "toggleBold": "Cmd-B",
15037 "toggleItalic": "Cmd-I",
15038 "drawLink": "Cmd-K",
15039 "toggleHeadingSmaller": "Cmd-H",
15040 "toggleHeadingBigger": "Shift-Cmd-H",
15041 "cleanBlock": "Cmd-E",
15042 "drawImage": "Cmd-Alt-I",
15043 "toggleBlockquote": "Cmd-'",
15044 "toggleOrderedList": "Cmd-Alt-L",
15045 "toggleUnorderedList": "Cmd-L",
15046 "toggleCodeBlock": "Cmd-Alt-C",
15047 "togglePreview": "Cmd-P",
15048 "toggleSideBySide": "F9",
15049 "toggleFullScreen": "F11"
15052 var getBindingName = function(f) {
15053 for(var key in bindings) {
15054 if(bindings[key] === f) {
15061 var isMobile = function() {
15064 if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
15065 })(navigator.userAgent || navigator.vendor || window.opera);
15071 * Fix shortcut. Mac use Command, others use Ctrl.
15073 function fixShortcut(name) {
15075 name = name.replace("Ctrl", "Cmd");
15077 name = name.replace("Cmd", "Ctrl");
15084 * Create icon element for toolbar.
15086 function createIcon(options, enableTooltips, shortcuts) {
15087 options = options || {};
15088 var el = document.createElement("a");
15089 enableTooltips = (enableTooltips == undefined) ? true : enableTooltips;
15091 if(options.title && enableTooltips) {
15092 el.title = createTootlip(options.title, options.action, shortcuts);
15095 el.title = el.title.replace("Ctrl", "⌘");
15096 el.title = el.title.replace("Alt", "⌥");
15101 el.className = options.className;
15105 function createSep() {
15106 var el = document.createElement("i");
15107 el.className = "separator";
15108 el.innerHTML = "|";
15112 function createTootlip(title, action, shortcuts) {
15114 var tooltip = title;
15117 actionName = getBindingName(action);
15118 if(shortcuts[actionName]) {
15119 tooltip += " (" + fixShortcut(shortcuts[actionName]) + ")";
15127 * The state of CodeMirror at the given position.
15129 function getState(cm, pos) {
15130 pos = pos || cm.getCursor("start");
15131 var stat = cm.getTokenAt(pos);
15132 if(!stat.type) return {};
15134 var types = stat.type.split(" ");
15138 for(var i = 0; i < types.length; i++) {
15140 if(data === "strong") {
15142 } else if(data === "variable-2") {
15143 text = cm.getLine(pos.line);
15144 if(/^\s*\d+\.\s/.test(text)) {
15145 ret["ordered-list"] = true;
15147 ret["unordered-list"] = true;
15149 } else if(data === "atom") {
15151 } else if(data === "em") {
15153 } else if(data === "quote") {
15155 } else if(data === "strikethrough") {
15156 ret.strikethrough = true;
15157 } else if(data === "comment") {
15159 } else if(data === "link") {
15161 } else if(data === "tag") {
15163 } else if(data.match(/^header(\-[1-6])?$/)) {
15164 ret[data.replace("header", "heading")] = true;
15171 // Saved overflow setting
15172 var saved_overflow = "";
15175 * Toggle full screen of the editor.
15177 function toggleFullScreen(editor) {
15179 var cm = editor.codemirror;
15180 cm.setOption("fullScreen", !cm.getOption("fullScreen"));
15183 // Prevent scrolling on body during fullscreen active
15184 if(cm.getOption("fullScreen")) {
15185 saved_overflow = document.body.style.overflow;
15186 document.body.style.overflow = "hidden";
15188 document.body.style.overflow = saved_overflow;
15192 // Update toolbar class
15193 var wrap = cm.getWrapperElement();
15195 if(!/fullscreen/.test(wrap.previousSibling.className)) {
15196 wrap.previousSibling.className += " fullscreen";
15198 wrap.previousSibling.className = wrap.previousSibling.className.replace(/\s*fullscreen\b/, "");
15202 // Update toolbar button
15203 var toolbarButton = editor.toolbarElements.fullscreen;
15205 if(!/active/.test(toolbarButton.className)) {
15206 toolbarButton.className += " active";
15208 toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
15212 // Hide side by side if needed
15213 var sidebyside = cm.getWrapperElement().nextSibling;
15214 if(/editor-preview-active-side/.test(sidebyside.className))
15215 toggleSideBySide(editor);
15220 * Action for toggling bold.
15222 function toggleBold(editor) {
15223 _toggleBlock(editor, "bold", editor.options.blockStyles.bold);
15228 * Action for toggling italic.
15230 function toggleItalic(editor) {
15231 _toggleBlock(editor, "italic", editor.options.blockStyles.italic);
15236 * Action for toggling strikethrough.
15238 function toggleStrikethrough(editor) {
15239 _toggleBlock(editor, "strikethrough", "~~");
15243 * Action for toggling code block.
15245 function toggleCodeBlock(editor) {
15246 var fenceCharsToInsert = editor.options.blockStyles.code;
15248 function fencing_line(line) {
15249 /* return true, if this is a ``` or ~~~ line */
15250 if(typeof line !== "object") {
15251 throw "fencing_line() takes a 'line' object (not a line number, or line text). Got: " + typeof line + ": " + line;
15253 return line.styles && line.styles[2] && line.styles[2].indexOf("formatting-code-block") !== -1;
15256 function token_state(token) {
15257 // base goes an extra level deep when mode backdrops are used, e.g. spellchecker on
15258 return token.state.base.base || token.state.base;
15261 function code_type(cm, line_num, line, firstTok, lastTok) {
15263 * Return "single", "indented", "fenced" or false
15265 * cm and line_num are required. Others are optional for efficiency
15266 * To check in the middle of a line, pass in firstTok yourself.
15268 line = line || cm.getLineHandle(line_num);
15269 firstTok = firstTok || cm.getTokenAt({
15273 lastTok = lastTok || (!!line.text && cm.getTokenAt({
15275 ch: line.text.length - 1
15277 var types = firstTok.type ? firstTok.type.split(" ") : [];
15278 if(lastTok && token_state(lastTok).indentedCode) {
15279 // have to check last char, since first chars of first line aren"t marked as indented
15281 } else if(types.indexOf("comment") === -1) {
15282 // has to be after "indented" check, since first chars of first indented line aren"t marked as such
15284 } else if(token_state(firstTok).fencedChars || token_state(lastTok).fencedChars || fencing_line(line)) {
15291 function insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert) {
15292 var start_line_sel = cur_start.line + 1,
15293 end_line_sel = cur_end.line + 1,
15294 sel_multi = cur_start.line !== cur_end.line,
15295 repl_start = fenceCharsToInsert + "\n",
15296 repl_end = "\n" + fenceCharsToInsert;
15300 // handle last char including \n or not
15301 if(sel_multi && cur_end.ch === 0) {
15302 repl_end = fenceCharsToInsert + "\n";
15305 _replaceSelection(cm, false, [repl_start, repl_end]);
15307 line: start_line_sel,
15310 line: end_line_sel,
15315 var cm = editor.codemirror,
15316 cur_start = cm.getCursor("start"),
15317 cur_end = cm.getCursor("end"),
15318 tok = cm.getTokenAt({
15319 line: cur_start.line,
15320 ch: cur_start.ch || 1
15321 }), // avoid ch 0 which is a cursor pos but not token
15322 line = cm.getLineHandle(cur_start.line),
15323 is_code = code_type(cm, cur_start.line, line, tok);
15324 var block_start, block_end, lineCount;
15326 if(is_code === "single") {
15327 // similar to some SimpleMDE _toggleBlock logic
15328 var start = line.text.slice(0, cur_start.ch).replace("`", ""),
15329 end = line.text.slice(cur_start.ch).replace("`", "");
15330 cm.replaceRange(start + end, {
15331 line: cur_start.line,
15334 line: cur_start.line,
15338 if(cur_start !== cur_end) {
15341 cm.setSelection(cur_start, cur_end);
15343 } else if(is_code === "fenced") {
15344 if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
15347 // find the fenced line so we know what type it is (tilde, backticks, number of them)
15348 for(block_start = cur_start.line; block_start >= 0; block_start--) {
15349 line = cm.getLineHandle(block_start);
15350 if(fencing_line(line)) {
15354 var fencedTok = cm.getTokenAt({
15358 var fence_chars = token_state(fencedTok).fencedChars;
15359 var start_text, start_line;
15360 var end_text, end_line;
15361 // check for selection going up against fenced lines, in which case we don't want to add more fencing
15362 if(fencing_line(cm.getLineHandle(cur_start.line))) {
15364 start_line = cur_start.line;
15365 } else if(fencing_line(cm.getLineHandle(cur_start.line - 1))) {
15367 start_line = cur_start.line - 1;
15369 start_text = fence_chars + "\n";
15370 start_line = cur_start.line;
15372 if(fencing_line(cm.getLineHandle(cur_end.line))) {
15374 end_line = cur_end.line;
15375 if(cur_end.ch === 0) {
15378 } else if(cur_end.ch !== 0 && fencing_line(cm.getLineHandle(cur_end.line + 1))) {
15380 end_line = cur_end.line + 1;
15382 end_text = fence_chars + "\n";
15383 end_line = cur_end.line + 1;
15385 if(cur_end.ch === 0) {
15386 // full last line selected, putting cursor at beginning of next
15389 cm.operation(function() {
15390 // end line first, so that line numbers don't change
15391 cm.replaceRange(end_text, {
15395 line: end_line + (end_text ? 0 : 1),
15398 cm.replaceRange(start_text, {
15402 line: start_line + (start_text ? 0 : 1),
15407 line: start_line + (start_text ? 1 : 0),
15410 line: end_line + (start_text ? 1 : -1),
15415 // no selection, search for ends of this fenced block
15416 var search_from = cur_start.line;
15417 if(fencing_line(cm.getLineHandle(cur_start.line))) { // gets a little tricky if cursor is right on a fenced line
15418 if(code_type(cm, cur_start.line + 1) === "fenced") {
15419 block_start = cur_start.line;
15420 search_from = cur_start.line + 1; // for searching for "end"
15422 block_end = cur_start.line;
15423 search_from = cur_start.line - 1; // for searching for "start"
15426 if(block_start === undefined) {
15427 for(block_start = search_from; block_start >= 0; block_start--) {
15428 line = cm.getLineHandle(block_start);
15429 if(fencing_line(line)) {
15434 if(block_end === undefined) {
15435 lineCount = cm.lineCount();
15436 for(block_end = search_from; block_end < lineCount; block_end++) {
15437 line = cm.getLineHandle(block_end);
15438 if(fencing_line(line)) {
15443 cm.operation(function() {
15444 cm.replaceRange("", {
15448 line: block_start + 1,
15451 cm.replaceRange("", {
15452 line: block_end - 1,
15461 } else if(is_code === "indented") {
15462 if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
15464 block_start = cur_start.line;
15465 block_end = cur_end.line;
15466 if(cur_end.ch === 0) {
15470 // no selection, search for ends of this indented block
15471 for(block_start = cur_start.line; block_start >= 0; block_start--) {
15472 line = cm.getLineHandle(block_start);
15473 if(line.text.match(/^\s*$/)) {
15474 // empty or all whitespace - keep going
15477 if(code_type(cm, block_start, line) !== "indented") {
15483 lineCount = cm.lineCount();
15484 for(block_end = cur_start.line; block_end < lineCount; block_end++) {
15485 line = cm.getLineHandle(block_end);
15486 if(line.text.match(/^\s*$/)) {
15487 // empty or all whitespace - keep going
15490 if(code_type(cm, block_end, line) !== "indented") {
15497 // if we are going to un-indent based on a selected set of lines, and the next line is indented too, we need to
15498 // insert a blank line so that the next line(s) continue to be indented code
15499 var next_line = cm.getLineHandle(block_end + 1),
15500 next_line_last_tok = next_line && cm.getTokenAt({
15501 line: block_end + 1,
15502 ch: next_line.text.length - 1
15504 next_line_indented = next_line_last_tok && token_state(next_line_last_tok).indentedCode;
15505 if(next_line_indented) {
15506 cm.replaceRange("\n", {
15507 line: block_end + 1,
15512 for(var i = block_start; i <= block_end; i++) {
15513 cm.indentLine(i, "subtract"); // TODO: this doesn't get tracked in the history, so can't be undone :(
15517 // insert code formatting
15518 var no_sel_and_starting_of_line = (cur_start.line === cur_end.line && cur_start.ch === cur_end.ch && cur_start.ch === 0);
15519 var sel_multi = cur_start.line !== cur_end.line;
15520 if(no_sel_and_starting_of_line || sel_multi) {
15521 insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert);
15523 _replaceSelection(cm, false, ["`", "`"]);
15529 * Action for toggling blockquote.
15531 function toggleBlockquote(editor) {
15532 var cm = editor.codemirror;
15533 _toggleLine(cm, "quote");
15537 * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal
15539 function toggleHeadingSmaller(editor) {
15540 var cm = editor.codemirror;
15541 _toggleHeading(cm, "smaller");
15545 * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal
15547 function toggleHeadingBigger(editor) {
15548 var cm = editor.codemirror;
15549 _toggleHeading(cm, "bigger");
15553 * Action for toggling heading size 1
15555 function toggleHeading1(editor) {
15556 var cm = editor.codemirror;
15557 _toggleHeading(cm, undefined, 1);
15561 * Action for toggling heading size 2
15563 function toggleHeading2(editor) {
15564 var cm = editor.codemirror;
15565 _toggleHeading(cm, undefined, 2);
15569 * Action for toggling heading size 3
15571 function toggleHeading3(editor) {
15572 var cm = editor.codemirror;
15573 _toggleHeading(cm, undefined, 3);
15578 * Action for toggling ul.
15580 function toggleUnorderedList(editor) {
15581 var cm = editor.codemirror;
15582 _toggleLine(cm, "unordered-list");
15587 * Action for toggling ol.
15589 function toggleOrderedList(editor) {
15590 var cm = editor.codemirror;
15591 _toggleLine(cm, "ordered-list");
15595 * Action for clean block (remove headline, list, blockquote code, markers)
15597 function cleanBlock(editor) {
15598 var cm = editor.codemirror;
15603 * Action for drawing a link.
15605 function drawLink(editor) {
15606 var cm = editor.codemirror;
15607 var stat = getState(cm);
15608 var options = editor.options;
15609 var url = "http://";
15610 if(options.promptURLs) {
15611 url = prompt(options.promptTexts.link);
15616 _replaceSelection(cm, stat.link, options.insertTexts.link, url);
15620 * Action for drawing an img.
15622 function drawImage(editor) {
15623 var cm = editor.codemirror;
15624 var stat = getState(cm);
15625 var options = editor.options;
15626 var url = "http://";
15627 if(options.promptURLs) {
15628 url = prompt(options.promptTexts.image);
15633 _replaceSelection(cm, stat.image, options.insertTexts.image, url);
15637 * Action for drawing a table.
15639 function drawTable(editor) {
15640 var cm = editor.codemirror;
15641 var stat = getState(cm);
15642 var options = editor.options;
15643 _replaceSelection(cm, stat.table, options.insertTexts.table);
15647 * Action for drawing a horizontal rule.
15649 function drawHorizontalRule(editor) {
15650 var cm = editor.codemirror;
15651 var stat = getState(cm);
15652 var options = editor.options;
15653 _replaceSelection(cm, stat.image, options.insertTexts.horizontalRule);
15660 function undo(editor) {
15661 var cm = editor.codemirror;
15670 function redo(editor) {
15671 var cm = editor.codemirror;
15678 * Toggle side by side preview
15680 function toggleSideBySide(editor) {
15681 var cm = editor.codemirror;
15682 var wrapper = cm.getWrapperElement();
15683 var preview = wrapper.nextSibling;
15684 var toolbarButton = editor.toolbarElements["side-by-side"];
15685 var useSideBySideListener = false;
15686 if(/editor-preview-active-side/.test(preview.className)) {
15687 preview.className = preview.className.replace(
15688 /\s*editor-preview-active-side\s*/g, ""
15690 toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, "");
15691 wrapper.className = wrapper.className.replace(/\s*CodeMirror-sided\s*/g, " ");
15693 // When the preview button is clicked for the first time,
15694 // give some time for the transition from editor.css to fire and the view to slide from right to left,
15695 // instead of just appearing.
15696 setTimeout(function() {
15697 if(!cm.getOption("fullScreen"))
15698 toggleFullScreen(editor);
15699 preview.className += " editor-preview-active-side";
15701 toolbarButton.className += " active";
15702 wrapper.className += " CodeMirror-sided";
15703 useSideBySideListener = true;
15706 // Hide normal preview if active
15707 var previewNormal = wrapper.lastChild;
15708 if(/editor-preview-active/.test(previewNormal.className)) {
15709 previewNormal.className = previewNormal.className.replace(
15710 /\s*editor-preview-active\s*/g, ""
15712 var toolbar = editor.toolbarElements.preview;
15713 var toolbar_div = wrapper.previousSibling;
15714 toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
15715 toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
15718 var sideBySideRenderingFunction = function() {
15719 preview.innerHTML = editor.options.previewRender(editor.value(), preview);
15722 if(!cm.sideBySideRenderingFunction) {
15723 cm.sideBySideRenderingFunction = sideBySideRenderingFunction;
15726 if(useSideBySideListener) {
15727 preview.innerHTML = editor.options.previewRender(editor.value(), preview);
15728 cm.on("update", cm.sideBySideRenderingFunction);
15730 cm.off("update", cm.sideBySideRenderingFunction);
15733 // Refresh to fix selection being off (#309)
15741 function togglePreview(editor) {
15742 var cm = editor.codemirror;
15743 var wrapper = cm.getWrapperElement();
15744 var toolbar_div = wrapper.previousSibling;
15745 var toolbar = editor.options.toolbar ? editor.toolbarElements.preview : false;
15746 var preview = wrapper.lastChild;
15747 if(!preview || !/editor-preview/.test(preview.className)) {
15748 preview = document.createElement("div");
15749 preview.className = "editor-preview";
15750 wrapper.appendChild(preview);
15752 if(/editor-preview-active/.test(preview.className)) {
15753 preview.className = preview.className.replace(
15754 /\s*editor-preview-active\s*/g, ""
15757 toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
15758 toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, "");
15761 // When the preview button is clicked for the first time,
15762 // give some time for the transition from editor.css to fire and the view to slide from right to left,
15763 // instead of just appearing.
15764 setTimeout(function() {
15765 preview.className += " editor-preview-active";
15768 toolbar.className += " active";
15769 toolbar_div.className += " disabled-for-preview";
15772 preview.innerHTML = editor.options.previewRender(editor.value(), preview);
15774 // Turn off side by side if needed
15775 var sidebyside = cm.getWrapperElement().nextSibling;
15776 if(/editor-preview-active-side/.test(sidebyside.className))
15777 toggleSideBySide(editor);
15780 function _replaceSelection(cm, active, startEnd, url) {
15781 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
15785 var start = startEnd[0];
15786 var end = startEnd[1];
15787 var startPoint = cm.getCursor("start");
15788 var endPoint = cm.getCursor("end");
15790 end = end.replace("#url#", url);
15793 text = cm.getLine(startPoint.line);
15794 start = text.slice(0, startPoint.ch);
15795 end = text.slice(startPoint.ch);
15796 cm.replaceRange(start + end, {
15797 line: startPoint.line,
15801 text = cm.getSelection();
15802 cm.replaceSelection(start + text + end);
15804 startPoint.ch += start.length;
15805 if(startPoint !== endPoint) {
15806 endPoint.ch += start.length;
15809 cm.setSelection(startPoint, endPoint);
15814 function _toggleHeading(cm, direction, size) {
15815 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
15818 var startPoint = cm.getCursor("start");
15819 var endPoint = cm.getCursor("end");
15820 for(var i = startPoint.line; i <= endPoint.line; i++) {
15822 var text = cm.getLine(i);
15823 var currHeadingLevel = text.search(/[^#]/);
15825 if(direction !== undefined) {
15826 if(currHeadingLevel <= 0) {
15827 if(direction == "bigger") {
15828 text = "###### " + text;
15830 text = "# " + text;
15832 } else if(currHeadingLevel == 6 && direction == "smaller") {
15833 text = text.substr(7);
15834 } else if(currHeadingLevel == 1 && direction == "bigger") {
15835 text = text.substr(2);
15837 if(direction == "bigger") {
15838 text = text.substr(1);
15845 if(currHeadingLevel <= 0) {
15846 text = "# " + text;
15847 } else if(currHeadingLevel == size) {
15848 text = text.substr(currHeadingLevel + 1);
15850 text = "# " + text.substr(currHeadingLevel + 1);
15852 } else if(size == 2) {
15853 if(currHeadingLevel <= 0) {
15854 text = "## " + text;
15855 } else if(currHeadingLevel == size) {
15856 text = text.substr(currHeadingLevel + 1);
15858 text = "## " + text.substr(currHeadingLevel + 1);
15861 if(currHeadingLevel <= 0) {
15862 text = "### " + text;
15863 } else if(currHeadingLevel == size) {
15864 text = text.substr(currHeadingLevel + 1);
15866 text = "### " + text.substr(currHeadingLevel + 1);
15871 cm.replaceRange(text, {
15884 function _toggleLine(cm, name) {
15885 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
15888 var stat = getState(cm);
15889 var startPoint = cm.getCursor("start");
15890 var endPoint = cm.getCursor("end");
15892 "quote": /^(\s*)\>\s+/,
15893 "unordered-list": /^(\s*)(\*|\-|\+)\s+/,
15894 "ordered-list": /^(\s*)\d+\.\s+/
15898 "unordered-list": "* ",
15899 "ordered-list": "1. "
15901 for(var i = startPoint.line; i <= endPoint.line; i++) {
15903 var text = cm.getLine(i);
15905 text = text.replace(repl[name], "$1");
15907 text = map[name] + text;
15909 cm.replaceRange(text, {
15921 function _toggleBlock(editor, type, start_chars, end_chars) {
15922 if(/editor-preview-active/.test(editor.codemirror.getWrapperElement().lastChild.className))
15925 end_chars = (typeof end_chars === "undefined") ? start_chars : end_chars;
15926 var cm = editor.codemirror;
15927 var stat = getState(cm);
15930 var start = start_chars;
15931 var end = end_chars;
15933 var startPoint = cm.getCursor("start");
15934 var endPoint = cm.getCursor("end");
15937 text = cm.getLine(startPoint.line);
15938 start = text.slice(0, startPoint.ch);
15939 end = text.slice(startPoint.ch);
15940 if(type == "bold") {
15941 start = start.replace(/(\*\*|__)(?![\s\S]*(\*\*|__))/, "");
15942 end = end.replace(/(\*\*|__)/, "");
15943 } else if(type == "italic") {
15944 start = start.replace(/(\*|_)(?![\s\S]*(\*|_))/, "");
15945 end = end.replace(/(\*|_)/, "");
15946 } else if(type == "strikethrough") {
15947 start = start.replace(/(\*\*|~~)(?![\s\S]*(\*\*|~~))/, "");
15948 end = end.replace(/(\*\*|~~)/, "");
15950 cm.replaceRange(start + end, {
15951 line: startPoint.line,
15954 line: startPoint.line,
15958 if(type == "bold" || type == "strikethrough") {
15959 startPoint.ch -= 2;
15960 if(startPoint !== endPoint) {
15963 } else if(type == "italic") {
15964 startPoint.ch -= 1;
15965 if(startPoint !== endPoint) {
15970 text = cm.getSelection();
15971 if(type == "bold") {
15972 text = text.split("**").join("");
15973 text = text.split("__").join("");
15974 } else if(type == "italic") {
15975 text = text.split("*").join("");
15976 text = text.split("_").join("");
15977 } else if(type == "strikethrough") {
15978 text = text.split("~~").join("");
15980 cm.replaceSelection(start + text + end);
15982 startPoint.ch += start_chars.length;
15983 endPoint.ch = startPoint.ch + text.length;
15986 cm.setSelection(startPoint, endPoint);
15990 function _cleanBlock(cm) {
15991 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))
15994 var startPoint = cm.getCursor("start");
15995 var endPoint = cm.getCursor("end");
15998 for(var line = startPoint.line; line <= endPoint.line; line++) {
15999 text = cm.getLine(line);
16000 text = text.replace(/^[ ]*([# ]+|\*|\-|[> ]+|[0-9]+(.|\)))[ ]*/, "");
16002 cm.replaceRange(text, {
16012 // Merge the properties of one object into another.
16013 function _mergeProperties(target, source) {
16014 for(var property in source) {
16015 if(source.hasOwnProperty(property)) {
16016 if(source[property] instanceof Array) {
16017 target[property] = source[property].concat(target[property] instanceof Array ? target[property] : []);
16019 source[property] !== null &&
16020 typeof source[property] === "object" &&
16021 source[property].constructor === Object
16023 target[property] = _mergeProperties(target[property] || {}, source[property]);
16025 target[property] = source[property];
16033 // Merge an arbitrary number of objects into one.
16034 function extend(target) {
16035 for(var i = 1; i < arguments.length; i++) {
16036 target = _mergeProperties(target, arguments[i]);
16042 /* The right word count in respect for CJK. */
16043 function wordCount(data) {
16044 var pattern = /[a-zA-Z0-9_\u0392-\u03c9\u0410-\u04F9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g;
16045 var m = data.match(pattern);
16047 if(m === null) return count;
16048 for(var i = 0; i < m.length; i++) {
16049 if(m[i].charCodeAt(0) >= 0x4E00) {
16050 count += m[i].length;
16058 var toolbarBuiltInButtons = {
16061 action: toggleBold,
16062 className: "fa fa-bold",
16068 action: toggleItalic,
16069 className: "fa fa-italic",
16074 name: "strikethrough",
16075 action: toggleStrikethrough,
16076 className: "fa fa-strikethrough",
16077 title: "Strikethrough"
16081 action: toggleHeadingSmaller,
16082 className: "fa fa-header",
16086 "heading-smaller": {
16087 name: "heading-smaller",
16088 action: toggleHeadingSmaller,
16089 className: "fa fa-header fa-header-x fa-header-smaller",
16090 title: "Smaller Heading"
16092 "heading-bigger": {
16093 name: "heading-bigger",
16094 action: toggleHeadingBigger,
16095 className: "fa fa-header fa-header-x fa-header-bigger",
16096 title: "Bigger Heading"
16100 action: toggleHeading1,
16101 className: "fa fa-header fa-header-x fa-header-1",
16102 title: "Big Heading"
16106 action: toggleHeading2,
16107 className: "fa fa-header fa-header-x fa-header-2",
16108 title: "Medium Heading"
16112 action: toggleHeading3,
16113 className: "fa fa-header fa-header-x fa-header-3",
16114 title: "Small Heading"
16117 name: "separator-1"
16121 action: toggleCodeBlock,
16122 className: "fa fa-code",
16127 action: toggleBlockquote,
16128 className: "fa fa-quote-left",
16132 "unordered-list": {
16133 name: "unordered-list",
16134 action: toggleUnorderedList,
16135 className: "fa fa-list-ul",
16136 title: "Generic List",
16140 name: "ordered-list",
16141 action: toggleOrderedList,
16142 className: "fa fa-list-ol",
16143 title: "Numbered List",
16147 name: "clean-block",
16148 action: cleanBlock,
16149 className: "fa fa-eraser fa-clean-block",
16150 title: "Clean block"
16153 name: "separator-2"
16158 className: "fa fa-link",
16159 title: "Create Link",
16165 className: "fa fa-picture-o",
16166 title: "Insert Image",
16172 className: "fa fa-table",
16173 title: "Insert Table"
16175 "horizontal-rule": {
16176 name: "horizontal-rule",
16177 action: drawHorizontalRule,
16178 className: "fa fa-minus",
16179 title: "Insert Horizontal Line"
16182 name: "separator-3"
16186 action: togglePreview,
16187 className: "fa fa-eye no-disable",
16188 title: "Toggle Preview",
16192 name: "side-by-side",
16193 action: toggleSideBySide,
16194 className: "fa fa-columns no-disable no-mobile",
16195 title: "Toggle Side by Side",
16199 name: "fullscreen",
16200 action: toggleFullScreen,
16201 className: "fa fa-arrows-alt no-disable no-mobile",
16202 title: "Toggle Fullscreen",
16206 name: "separator-4"
16210 action: "https://simplemde.com/markdown-guide",
16211 className: "fa fa-question-circle",
16212 title: "Markdown Guide",
16216 name: "separator-5"
16221 className: "fa fa-undo no-disable",
16227 className: "fa fa-repeat no-disable",
16232 var insertTexts = {
16233 link: ["[", "](#url#)"],
16234 image: ["![](", "#url#)"],
16235 table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n\n"],
16236 horizontalRule: ["", "\n\n-----\n\n"]
16239 var promptTexts = {
16240 link: "URL for the link:",
16241 image: "URL of the image:"
16244 var blockStyles = {
16251 * Interface of SimpleMDE.
16253 function SimpleMDE(options) {
16254 // Handle options parameter
16255 options = options || {};
16258 // Used later to refer to it"s parent
16259 options.parent = this;
16262 // Check if Font Awesome needs to be auto downloaded
16263 var autoDownloadFA = true;
16265 if(options.autoDownloadFontAwesome === false) {
16266 autoDownloadFA = false;
16269 if(options.autoDownloadFontAwesome !== true) {
16270 var styleSheets = document.styleSheets;
16271 for(var i = 0; i < styleSheets.length; i++) {
16272 if(!styleSheets[i].href)
16275 if(styleSheets[i].href.indexOf("//maxcdn.bootstrapcdn.com/font-awesome/") > -1) {
16276 autoDownloadFA = false;
16281 if(autoDownloadFA) {
16282 var link = document.createElement("link");
16283 link.rel = "stylesheet";
16284 link.href = "https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css";
16285 document.getElementsByTagName("head")[0].appendChild(link);
16289 // Find the textarea to use
16290 if(options.element) {
16291 this.element = options.element;
16292 } else if(options.element === null) {
16293 // This means that the element option was specified, but no element was found
16294 console.log("SimpleMDE: Error. No element was found.");
16300 if(options.toolbar === undefined) {
16302 options.toolbar = [];
16305 // Loop over the built in buttons, to get the preferred order
16306 for(var key in toolbarBuiltInButtons) {
16307 if(toolbarBuiltInButtons.hasOwnProperty(key)) {
16308 if(key.indexOf("separator-") != -1) {
16309 options.toolbar.push("|");
16312 if(toolbarBuiltInButtons[key].default === true || (options.showIcons && options.showIcons.constructor === Array && options.showIcons.indexOf(key) != -1)) {
16313 options.toolbar.push(key);
16320 // Handle status bar
16321 if(!options.hasOwnProperty("status")) {
16322 options.status = ["autosave", "lines", "words", "cursor"];
16326 // Add default preview rendering function
16327 if(!options.previewRender) {
16328 options.previewRender = function(plainText) {
16329 // Note: "this" refers to the options object
16330 return this.parent.markdown(plainText);
16335 // Set default options for parsing config
16336 options.parsingConfig = extend({
16337 highlightFormatting: true // needed for toggleCodeBlock to detect types of code
16338 }, options.parsingConfig || {});
16341 // Merging the insertTexts, with the given options
16342 options.insertTexts = extend({}, insertTexts, options.insertTexts || {});
16345 // Merging the promptTexts, with the given options
16346 options.promptTexts = promptTexts;
16349 // Merging the blockStyles, with the given options
16350 options.blockStyles = extend({}, blockStyles, options.blockStyles || {});
16353 // Merging the shortcuts, with the given options
16354 options.shortcuts = extend({}, shortcuts, options.shortcuts || {});
16357 // Change unique_id to uniqueId for backwards compatibility
16358 if(options.autosave != undefined && options.autosave.unique_id != undefined && options.autosave.unique_id != "")
16359 options.autosave.uniqueId = options.autosave.unique_id;
16362 // Update this options
16363 this.options = options;
16370 // The codemirror component is only available after rendering
16371 // so, the setter for the initialValue can only run after
16372 // the element has been rendered
16373 if(options.initialValue && (!this.options.autosave || this.options.autosave.foundSavedValue !== true)) {
16374 this.value(options.initialValue);
16379 * Default markdown render.
16381 SimpleMDE.prototype.markdown = function(text) {
16384 var markedOptions = {};
16388 if(this.options && this.options.renderingConfig && this.options.renderingConfig.singleLineBreaks === false) {
16389 markedOptions.breaks = false;
16391 markedOptions.breaks = true;
16394 if(this.options && this.options.renderingConfig && this.options.renderingConfig.codeSyntaxHighlighting === true && window.hljs) {
16395 markedOptions.highlight = function(code) {
16396 return window.hljs.highlightAuto(code).value;
16402 marked.setOptions(markedOptions);
16406 return marked(text);
16411 * Render editor to the given element.
16413 SimpleMDE.prototype.render = function(el) {
16415 el = this.element || document.getElementsByTagName("textarea")[0];
16418 if(this._rendered && this._rendered === el) {
16419 // Already rendered.
16424 var options = this.options;
16429 for(var key in options.shortcuts) {
16430 // null stands for "do not bind this command"
16431 if(options.shortcuts[key] !== null && bindings[key] !== null) {
16433 keyMaps[fixShortcut(options.shortcuts[key])] = function() {
16434 bindings[key](self);
16440 keyMaps["Enter"] = "newlineAndIndentContinueMarkdownList";
16441 keyMaps["Tab"] = "tabAndIndentMarkdownList";
16442 keyMaps["Shift-Tab"] = "shiftTabAndUnindentMarkdownList";
16443 keyMaps["Esc"] = function(cm) {
16444 if(cm.getOption("fullScreen")) toggleFullScreen(self);
16447 document.addEventListener("keydown", function(e) {
16448 e = e || window.event;
16450 if(e.keyCode == 27) {
16451 if(self.codemirror.getOption("fullScreen")) toggleFullScreen(self);
16455 var mode, backdrop;
16456 if(options.spellChecker !== false) {
16457 mode = "spell-checker";
16458 backdrop = options.parsingConfig;
16459 backdrop.name = "gfm";
16460 backdrop.gitHubSpice = false;
16462 CodeMirrorSpellChecker({
16463 codeMirrorInstance: CodeMirror
16466 mode = options.parsingConfig;
16468 mode.gitHubSpice = false;
16471 this.codemirror = CodeMirror.fromTextArea(el, {
16473 backdrop: backdrop,
16475 tabSize: (options.tabSize != undefined) ? options.tabSize : 2,
16476 indentUnit: (options.tabSize != undefined) ? options.tabSize : 2,
16477 indentWithTabs: (options.indentWithTabs === false) ? false : true,
16478 lineNumbers: false,
16479 autofocus: (options.autofocus === true) ? true : false,
16480 extraKeys: keyMaps,
16481 lineWrapping: (options.lineWrapping === false) ? false : true,
16482 allowDropFileTypes: ["text/plain"],
16483 placeholder: options.placeholder || el.getAttribute("placeholder") || "",
16484 styleSelectedText: (options.styleSelectedText != undefined) ? options.styleSelectedText : true
16487 if(options.forceSync === true) {
16488 var cm = this.codemirror;
16489 cm.on("change", function() {
16496 if(options.toolbar !== false) {
16497 this.gui.toolbar = this.createToolbar();
16499 if(options.status !== false) {
16500 this.gui.statusbar = this.createStatusbar();
16502 if(options.autosave != undefined && options.autosave.enabled === true) {
16506 this.gui.sideBySide = this.createSideBySide();
16508 this._rendered = this.element;
16511 // Fixes CodeMirror bug (#344)
16512 var temp_cm = this.codemirror;
16513 setTimeout(function() {
16515 }.bind(temp_cm), 0);
16518 // Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem throw QuotaExceededError. We're going to detect this and set a variable accordingly.
16519 function isLocalStorageAvailable() {
16520 if(typeof localStorage === "object") {
16522 localStorage.setItem("smde_localStorage", 1);
16523 localStorage.removeItem("smde_localStorage");
16534 SimpleMDE.prototype.autosave = function() {
16535 if(isLocalStorageAvailable()) {
16536 var simplemde = this;
16538 if(this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == "") {
16539 console.log("SimpleMDE: You must set a uniqueId to use the autosave feature");
16543 if(simplemde.element.form != null && simplemde.element.form != undefined) {
16544 simplemde.element.form.addEventListener("submit", function() {
16545 localStorage.removeItem("smde_" + simplemde.options.autosave.uniqueId);
16549 if(this.options.autosave.loaded !== true) {
16550 if(typeof localStorage.getItem("smde_" + this.options.autosave.uniqueId) == "string" && localStorage.getItem("smde_" + this.options.autosave.uniqueId) != "") {
16551 this.codemirror.setValue(localStorage.getItem("smde_" + this.options.autosave.uniqueId));
16552 this.options.autosave.foundSavedValue = true;
16555 this.options.autosave.loaded = true;
16558 localStorage.setItem("smde_" + this.options.autosave.uniqueId, simplemde.value());
16560 var el = document.getElementById("autosaved");
16561 if(el != null && el != undefined && el != "") {
16562 var d = new Date();
16563 var hh = d.getHours();
16564 var m = d.getMinutes();
16574 m = m < 10 ? "0" + m : m;
16576 el.innerHTML = "Autosaved: " + h + ":" + m + " " + dd;
16579 this.autosaveTimeoutId = setTimeout(function() {
16580 simplemde.autosave();
16581 }, this.options.autosave.delay || 10000);
16583 console.log("SimpleMDE: localStorage not available, cannot autosave");
16587 SimpleMDE.prototype.clearAutosavedValue = function() {
16588 if(isLocalStorageAvailable()) {
16589 if(this.options.autosave == undefined || this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == "") {
16590 console.log("SimpleMDE: You must set a uniqueId to clear the autosave value");
16594 localStorage.removeItem("smde_" + this.options.autosave.uniqueId);
16596 console.log("SimpleMDE: localStorage not available, cannot autosave");
16600 SimpleMDE.prototype.createSideBySide = function() {
16601 var cm = this.codemirror;
16602 var wrapper = cm.getWrapperElement();
16603 var preview = wrapper.nextSibling;
16605 if(!preview || !/editor-preview-side/.test(preview.className)) {
16606 preview = document.createElement("div");
16607 preview.className = "editor-preview-side";
16608 wrapper.parentNode.insertBefore(preview, wrapper.nextSibling);
16611 // Syncs scroll editor -> preview
16612 var cScroll = false;
16613 var pScroll = false;
16614 cm.on("scroll", function(v) {
16620 var height = v.getScrollInfo().height - v.getScrollInfo().clientHeight;
16621 var ratio = parseFloat(v.getScrollInfo().top) / height;
16622 var move = (preview.scrollHeight - preview.clientHeight) * ratio;
16623 preview.scrollTop = move;
16626 // Syncs scroll preview -> editor
16627 preview.onscroll = function() {
16633 var height = preview.scrollHeight - preview.clientHeight;
16634 var ratio = parseFloat(preview.scrollTop) / height;
16635 var move = (cm.getScrollInfo().height - cm.getScrollInfo().clientHeight) * ratio;
16636 cm.scrollTo(0, move);
16641 SimpleMDE.prototype.createToolbar = function(items) {
16642 items = items || this.options.toolbar;
16644 if(!items || items.length === 0) {
16648 for(i = 0; i < items.length; i++) {
16649 if(toolbarBuiltInButtons[items[i]] != undefined) {
16650 items[i] = toolbarBuiltInButtons[items[i]];
16654 var bar = document.createElement("div");
16655 bar.className = "editor-toolbar";
16659 var toolbarData = {};
16660 self.toolbar = items;
16662 for(i = 0; i < items.length; i++) {
16663 if(items[i].name == "guide" && self.options.toolbarGuideIcon === false)
16666 if(self.options.hideIcons && self.options.hideIcons.indexOf(items[i].name) != -1)
16669 // Fullscreen does not work well on mobile devices (even tablets)
16670 // In the future, hopefully this can be resolved
16671 if((items[i].name == "fullscreen" || items[i].name == "side-by-side") && isMobile())
16675 // Don't include trailing separators
16676 if(items[i] === "|") {
16677 var nonSeparatorIconsFollow = false;
16679 for(var x = (i + 1); x < items.length; x++) {
16680 if(items[x] !== "|" && (!self.options.hideIcons || self.options.hideIcons.indexOf(items[x].name) == -1)) {
16681 nonSeparatorIconsFollow = true;
16685 if(!nonSeparatorIconsFollow)
16690 // Create the icon and append to the toolbar
16696 el = createIcon(item, self.options.toolbarTips, self.options.shortcuts);
16699 // bind events, special for info
16701 if(typeof item.action === "function") {
16702 el.onclick = function(e) {
16703 e.preventDefault();
16706 } else if(typeof item.action === "string") {
16707 el.href = item.action;
16708 el.target = "_blank";
16712 toolbarData[item.name || item] = el;
16713 bar.appendChild(el);
16717 self.toolbarElements = toolbarData;
16719 var cm = this.codemirror;
16720 cm.on("cursorActivity", function() {
16721 var stat = getState(cm);
16723 for(var key in toolbarData) {
16725 var el = toolbarData[key];
16727 el.className += " active";
16728 } else if(key != "fullscreen" && key != "side-by-side") {
16729 el.className = el.className.replace(/\s*active\s*/g, "");
16735 var cmWrapper = cm.getWrapperElement();
16736 cmWrapper.parentNode.insertBefore(bar, cmWrapper);
16740 SimpleMDE.prototype.createStatusbar = function(status) {
16742 status = status || this.options.status;
16743 var options = this.options;
16744 var cm = this.codemirror;
16747 // Make sure the status variable is valid
16748 if(!status || status.length === 0)
16752 // Set up the built-in items
16754 var i, onUpdate, defaultValue;
16756 for(i = 0; i < status.length; i++) {
16757 // Reset some values
16758 onUpdate = undefined;
16759 defaultValue = undefined;
16762 // Handle if custom or not
16763 if(typeof status[i] === "object") {
16765 className: status[i].className,
16766 defaultValue: status[i].defaultValue,
16767 onUpdate: status[i].onUpdate
16770 var name = status[i];
16772 if(name === "words") {
16773 defaultValue = function(el) {
16774 el.innerHTML = wordCount(cm.getValue());
16776 onUpdate = function(el) {
16777 el.innerHTML = wordCount(cm.getValue());
16779 } else if(name === "lines") {
16780 defaultValue = function(el) {
16781 el.innerHTML = cm.lineCount();
16783 onUpdate = function(el) {
16784 el.innerHTML = cm.lineCount();
16786 } else if(name === "cursor") {
16787 defaultValue = function(el) {
16788 el.innerHTML = "0:0";
16790 onUpdate = function(el) {
16791 var pos = cm.getCursor();
16792 el.innerHTML = pos.line + ":" + pos.ch;
16794 } else if(name === "autosave") {
16795 defaultValue = function(el) {
16796 if(options.autosave != undefined && options.autosave.enabled === true) {
16797 el.setAttribute("id", "autosaved");
16804 defaultValue: defaultValue,
16811 // Create element for the status bar
16812 var bar = document.createElement("div");
16813 bar.className = "editor-statusbar";
16816 // Create a new span for each item
16817 for(i = 0; i < items.length; i++) {
16818 // Store in temporary variable
16819 var item = items[i];
16822 // Create span element
16823 var el = document.createElement("span");
16824 el.className = item.className;
16827 // Ensure the defaultValue is a function
16828 if(typeof item.defaultValue === "function") {
16829 item.defaultValue(el);
16833 // Ensure the onUpdate is a function
16834 if(typeof item.onUpdate === "function") {
16835 // Create a closure around the span of the current action, then execute the onUpdate handler
16836 this.codemirror.on("update", (function(el, item) {
16837 return function() {
16844 // Append the item to the status bar
16845 bar.appendChild(el);
16849 // Insert the status bar into the DOM
16850 var cmWrapper = this.codemirror.getWrapperElement();
16851 cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling);
16856 * Get or set the text content.
16858 SimpleMDE.prototype.value = function(val) {
16859 if(val === undefined) {
16860 return this.codemirror.getValue();
16862 this.codemirror.getDoc().setValue(val);
16869 * Bind static methods for exports.
16871 SimpleMDE.toggleBold = toggleBold;
16872 SimpleMDE.toggleItalic = toggleItalic;
16873 SimpleMDE.toggleStrikethrough = toggleStrikethrough;
16874 SimpleMDE.toggleBlockquote = toggleBlockquote;
16875 SimpleMDE.toggleHeadingSmaller = toggleHeadingSmaller;
16876 SimpleMDE.toggleHeadingBigger = toggleHeadingBigger;
16877 SimpleMDE.toggleHeading1 = toggleHeading1;
16878 SimpleMDE.toggleHeading2 = toggleHeading2;
16879 SimpleMDE.toggleHeading3 = toggleHeading3;
16880 SimpleMDE.toggleCodeBlock = toggleCodeBlock;
16881 SimpleMDE.toggleUnorderedList = toggleUnorderedList;
16882 SimpleMDE.toggleOrderedList = toggleOrderedList;
16883 SimpleMDE.cleanBlock = cleanBlock;
16884 SimpleMDE.drawLink = drawLink;
16885 SimpleMDE.drawImage = drawImage;
16886 SimpleMDE.drawTable = drawTable;
16887 SimpleMDE.drawHorizontalRule = drawHorizontalRule;
16888 SimpleMDE.undo = undo;
16889 SimpleMDE.redo = redo;
16890 SimpleMDE.togglePreview = togglePreview;
16891 SimpleMDE.toggleSideBySide = toggleSideBySide;
16892 SimpleMDE.toggleFullScreen = toggleFullScreen;
16895 * Bind instance methods for exports.
16897 SimpleMDE.prototype.toggleBold = function() {
16900 SimpleMDE.prototype.toggleItalic = function() {
16901 toggleItalic(this);
16903 SimpleMDE.prototype.toggleStrikethrough = function() {
16904 toggleStrikethrough(this);
16906 SimpleMDE.prototype.toggleBlockquote = function() {
16907 toggleBlockquote(this);
16909 SimpleMDE.prototype.toggleHeadingSmaller = function() {
16910 toggleHeadingSmaller(this);
16912 SimpleMDE.prototype.toggleHeadingBigger = function() {
16913 toggleHeadingBigger(this);
16915 SimpleMDE.prototype.toggleHeading1 = function() {
16916 toggleHeading1(this);
16918 SimpleMDE.prototype.toggleHeading2 = function() {
16919 toggleHeading2(this);
16921 SimpleMDE.prototype.toggleHeading3 = function() {
16922 toggleHeading3(this);
16924 SimpleMDE.prototype.toggleCodeBlock = function() {
16925 toggleCodeBlock(this);
16927 SimpleMDE.prototype.toggleUnorderedList = function() {
16928 toggleUnorderedList(this);
16930 SimpleMDE.prototype.toggleOrderedList = function() {
16931 toggleOrderedList(this);
16933 SimpleMDE.prototype.cleanBlock = function() {
16936 SimpleMDE.prototype.drawLink = function() {
16939 SimpleMDE.prototype.drawImage = function() {
16942 SimpleMDE.prototype.drawTable = function() {
16945 SimpleMDE.prototype.drawHorizontalRule = function() {
16946 drawHorizontalRule(this);
16948 SimpleMDE.prototype.undo = function() {
16951 SimpleMDE.prototype.redo = function() {
16954 SimpleMDE.prototype.togglePreview = function() {
16955 togglePreview(this);
16957 SimpleMDE.prototype.toggleSideBySide = function() {
16958 toggleSideBySide(this);
16960 SimpleMDE.prototype.toggleFullScreen = function() {
16961 toggleFullScreen(this);
16964 SimpleMDE.prototype.isPreviewActive = function() {
16965 var cm = this.codemirror;
16966 var wrapper = cm.getWrapperElement();
16967 var preview = wrapper.lastChild;
16969 return /editor-preview-active/.test(preview.className);
16972 SimpleMDE.prototype.isSideBySideActive = function() {
16973 var cm = this.codemirror;
16974 var wrapper = cm.getWrapperElement();
16975 var preview = wrapper.nextSibling;
16977 return /editor-preview-active-side/.test(preview.className);
16980 SimpleMDE.prototype.isFullscreenActive = function() {
16981 var cm = this.codemirror;
16983 return cm.getOption("fullScreen");
16986 SimpleMDE.prototype.getState = function() {
16987 var cm = this.codemirror;
16989 return getState(cm);
16992 SimpleMDE.prototype.toTextArea = function() {
16993 var cm = this.codemirror;
16994 var wrapper = cm.getWrapperElement();
16996 if(wrapper.parentNode) {
16997 if(this.gui.toolbar) {
16998 wrapper.parentNode.removeChild(this.gui.toolbar);
17000 if(this.gui.statusbar) {
17001 wrapper.parentNode.removeChild(this.gui.statusbar);
17003 if(this.gui.sideBySide) {
17004 wrapper.parentNode.removeChild(this.gui.sideBySide);
17010 if(this.autosaveTimeoutId) {
17011 clearTimeout(this.autosaveTimeoutId);
17012 this.autosaveTimeoutId = undefined;
17013 this.clearAutosavedValue();
17017 module.exports = SimpleMDE;
17018 },{"./codemirror/tablist":19,"codemirror":10,"codemirror-spell-checker":4,"codemirror/addon/display/fullscreen.js":5,"codemirror/addon/display/placeholder.js":6,"codemirror/addon/edit/continuelist.js":7,"codemirror/addon/mode/overlay.js":8,"codemirror/addon/selection/mark-selection.js":9,"codemirror/mode/gfm/gfm.js":11,"codemirror/mode/markdown/markdown.js":12,"codemirror/mode/xml/xml.js":14,"marked":17}]},{},[20])(20)