vendor: 5,391 bytes, lines 1-152
1/** 2 * simplemde v1.11.2 3 * Copyright Next Step Webs, Inc. 4 * @link https://github.com/NextStepWebs/simplemde-markdown-editor 5 * @license MIT 6 */ 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){ 8'use strict' 9 10exports.toByteArray = toByteArray 11exports.fromByteArray = fromByteArray 12 13var lookup = [] 14var revLookup = [] 15var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array 16 17function init () { 18 var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 19 for (var i = 0, len = code.length; i < len; ++i) { 20 lookup[i] = code[i] 21 revLookup[code.charCodeAt(i)] = i 22 } 23 24 revLookup['-'.charCodeAt(0)] = 62 25 revLookup['_'.charCodeAt(0)] = 63 26} 27 28init() 29 30function toByteArray (b64) { 31 var i, j, l, tmp, placeHolders, arr 32 var len = b64.length 33 34 if (len % 4 > 0) { 35 throw new Error('Invalid string. Length must be a multiple of 4') 36 } 37 38 // the number of equal signs (place holders) 39 // if there are two placeholders, than the two characters before it 40 // represent one byte 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 44 45 // base64 is 4/3 + up to two characters of the original data 46 arr = new Arr(len * 3 / 4 - placeHolders) 47 48 // if there are placeholders, only get up to the last complete 4 chars 49 l = placeHolders > 0 ? len - 4 : len 50 51 var L = 0 52 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 57 arr[L++] = tmp & 0xFF 58 } 59 60 if (placeHolders === 2) { 61 tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) 62 arr[L++] = tmp & 0xFF 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 66 arr[L++] = tmp & 0xFF 67 } 68 69 return arr 70} 71 72function tripletToBase64 (num) { 73 return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] 74} 75 76function encodeChunk (uint8, start, end) { 77 var tmp 78 var output = [] 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)) 82 } 83 return output.join('') 84} 85 86function fromByteArray (uint8) { 87 var tmp 88 var len = uint8.length 89 var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes 90 var output = '' 91 var parts = [] 92 var maxChunkLength = 16383 // must be multiple of 3 93 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))) 97 } 98 99 // pad the end with zeros, but make sure to not forget the extra bytes 100 if (extraBytes === 1) { 101 tmp = uint8[len - 1] 102 output += lookup[tmp >> 2] 103 output += lookup[(tmp << 4) & 0x3F] 104 output += '==' 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] 110 output += '=' 111 } 112 113 parts.push(output) 114 115 return parts.join('') 116} 117 118},{}],2:[function(require,module,exports){ 119 120},{}],3:[function(require,module,exports){ 121(function (global){ 122/*! 123 * The buffer module from node.js, for the browser. 124 * 125 * @author Feross Aboukhadijeh <[email protected]> <http://feross.org> 126 * @license MIT 127 */ 128/* eslint-disable no-proto */ 129 130'use strict' 131 132var base64 = require('base64-js') 133var ieee754 = require('ieee754') 134var isArray = require('isarray') 135 136exports.Buffer = Buffer 137exports.SlowBuffer = SlowBuffer 138exports.INSPECT_MAX_BYTES = 50 139 140/** 141 * If `Buffer.TYPED_ARRAY_SUPPORT`: 142 * === true Use Uint8Array implementation (fastest) 143 * === false Use Object implementation (most compatible, even IE6) 144 * 145 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, 146 * Opera 11.6+, iOS 4.2+. 147 * 148 * Due to various browser bugs, sometimes the Object implementation will be used even 149 * when the browser supports typed arrays. 150 * 151 * Note: 152 *
vendor: 10,106 bytes, lines 153-520
153 * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, 154 * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. 155 * 156 * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. 157 * 158 * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of 159 * incorrect length in some situations. 160 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. 163 */ 164Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined 165 ? global.TYPED_ARRAY_SUPPORT 166 : typedArraySupport() 167 168/* 169 * Export kMaxLength after typed array support is determined. 170 */ 171exports.kMaxLength = kMaxLength() 172 173function typedArraySupport () { 174 try { 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` 180 } catch (e) { 181 return false 182 } 183} 184 185function kMaxLength () { 186 return Buffer.TYPED_ARRAY_SUPPORT 187 ? 0x7fffffff 188 : 0x3fffffff 189} 190 191function createBuffer (that, length) { 192 if (kMaxLength() < length) { 193 throw new RangeError('Invalid typed array length') 194 } 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 199 } else { 200 // Fallback: Return an object instance of the Buffer class 201 if (that === null) { 202 that = new Buffer(length) 203 } 204 that.length = length 205 } 206 207 return that 208} 209 210/** 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. 216 * 217 * The `Uint8Array` prototype remains unmodified. 218 */ 219 220function Buffer (arg, encodingOrOffset, length) { 221 if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) { 222 return new Buffer(arg, encodingOrOffset, length) 223 } 224 225 // Common case. 226 if (typeof arg === 'number') { 227 if (typeof encodingOrOffset === 'string') { 228 throw new Error( 229 'If encoding is specified then the first argument must be a string' 230 ) 231 } 232 return allocUnsafe(this, arg) 233 } 234 return from(this, arg, encodingOrOffset, length) 235} 236 237Buffer.poolSize = 8192 // not used by this implementation 238 239// TODO: Legacy, not needed anymore. Remove in next major version. 240Buffer._augment = function (arr) { 241 arr.__proto__ = Buffer.prototype 242 return arr 243} 244 245function from (that, value, encodingOrOffset, length) { 246 if (typeof value === 'number') { 247 throw new TypeError('"value" argument must not be a number') 248 } 249 250 if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { 251 return fromArrayBuffer(that, value, encodingOrOffset, length) 252 } 253 254 if (typeof value === 'string') { 255 return fromString(that, value, encodingOrOffset) 256 } 257 258 return fromObject(that, value) 259} 260 261/** 262 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError 263 * if value is a number. 264 * Buffer.from(str[, encoding]) 265 * Buffer.from(array) 266 * Buffer.from(buffer) 267 * Buffer.from(arrayBuffer[, byteOffset[, length]]) 268 **/ 269Buffer.from = function (value, encodingOrOffset, length) { 270 return from(null, value, encodingOrOffset, length) 271} 272 273if (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, { 280 value: null, 281 configurable: true 282 }) 283 } 284} 285 286function assertSize (size) { 287 if (typeof size !== 'number') { 288 throw new TypeError('"size" argument must be a number') 289 } 290} 291 292function alloc (that, size, fill, encoding) { 293 assertSize(size) 294 if (size <= 0) { 295 return createBuffer(that, size) 296 } 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) 304 } 305 return createBuffer(that, size) 306} 307 308/** 309 * Creates a new filled Buffer instance. 310 * alloc(size[, fill[, encoding]]) 311 **/ 312Buffer.alloc = function (size, fill, encoding) { 313 return alloc(null, size, fill, encoding) 314} 315 316function allocUnsafe (that, size) { 317 assertSize(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++) { 321 that[i] = 0 322 } 323 } 324 return that 325} 326 327/** 328 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. 329 * */ 330Buffer.allocUnsafe = function (size) { 331 return allocUnsafe(null, size) 332} 333/** 334 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. 335 */ 336Buffer.allocUnsafeSlow = function (size) { 337 return allocUnsafe(null, size) 338} 339 340function fromString (that, string, encoding) { 341 if (typeof encoding !== 'string' || encoding === '') { 342 encoding = 'utf8' 343 } 344 345 if (!Buffer.isEncoding(encoding)) { 346 throw new TypeError('"encoding" must be a valid string encoding') 347 } 348 349 var length = byteLength(string, encoding) | 0 350 that = createBuffer(that, length) 351 352 that.write(string, encoding) 353 return that 354} 355 356function 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 361 } 362 return that 363} 364 365function fromArrayBuffer (that, array, byteOffset, length) { 366 array.byteLength // this throws if `array` is not a valid ArrayBuffer 367 368 if (byteOffset < 0 || array.byteLength < byteOffset) { 369 throw new RangeError('\'offset\' is out of bounds') 370 } 371 372 if (array.byteLength < byteOffset + (length || 0)) { 373 throw new RangeError('\'length\' is out of bounds') 374 } 375 376 if (length === undefined) { 377 array = new Uint8Array(array, byteOffset) 378 } else { 379 array = new Uint8Array(array, byteOffset, length) 380 } 381 382 if (Buffer.TYPED_ARRAY_SUPPORT) { 383 // Return an augmented `Uint8Array` instance, for best performance 384 that = array 385 that.__proto__ = Buffer.prototype 386 } else { 387 // Fallback: Return an object instance of the Buffer class 388 that = fromArrayLike(that, array) 389 } 390 return that 391} 392 393function fromObject (that, obj) { 394 if (Buffer.isBuffer(obj)) { 395 var len = checked(obj.length) | 0 396 that = createBuffer(that, len) 397 398 if (that.length === 0) { 399 return that 400 } 401 402 obj.copy(that, 0, 0, len) 403 return that 404 } 405 406 if (obj) { 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) 411 } 412 return fromArrayLike(that, obj) 413 } 414 415 if (obj.type === 'Buffer' && isArray(obj.data)) { 416 return fromArrayLike(that, obj.data) 417 } 418 } 419 420 throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') 421} 422 423function 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') 429 } 430 return length | 0 431} 432 433function SlowBuffer (length) { 434 if (+length != length) { // eslint-disable-line eqeqeq 435 length = 0 436 } 437 return Buffer.alloc(+length) 438} 439 440Buffer.isBuffer = function isBuffer (b) { 441 return !!(b != null && b._isBuffer) 442} 443 444Buffer.compare = function compare (a, b) { 445 if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { 446 throw new TypeError('Arguments must be Buffers') 447 } 448 449 if (a === b) return 0 450 451 var x = a.length 452 var y = b.length 453 454 for (var i = 0, len = Math.min(x, y); i < len; ++i) { 455 if (a[i] !== b[i]) { 456 x = a[i] 457 y = b[i] 458 break 459 } 460 } 461 462 if (x < y) return -1 463 if (y < x) return 1 464 return 0 465} 466 467Buffer.isEncoding = function isEncoding (encoding) { 468 switch (String(encoding).toLowerCase()) { 469 case 'hex': 470 case 'utf8': 471 case 'utf-8': 472 case 'ascii': 473 case 'binary': 474 case 'base64': 475 case 'raw': 476 case 'ucs2': 477 case 'ucs-2': 478 case 'utf16le': 479 case 'utf-16le': 480 return true 481 default: 482 return false 483 } 484} 485 486Buffer.concat = function concat (list, length) { 487 if (!isArray(list)) { 488 throw new TypeError('"list" argument must be an Array of Buffers') 489 } 490 491 if (list.length === 0) { 492 return Buffer.alloc(0) 493 } 494 495 var i 496 if (length === undefined) { 497 length = 0 498 for (i = 0; i < list.length; i++) { 499 length += list[i].length 500 } 501 } 502 503 var buffer = Buffer.allocUnsafe(length) 504 var pos = 0 505 for (i = 0; i < list.length; i++) { 506 var buf = list[i] 507 if (!Buffer.isBuffer(buf)) { 508 throw new TypeError('"list" argument must be an Array of Buffers') 509 } 510 buf.copy(buffer, pos) 511 pos += buf.length 512 } 513 return buffer 514} 515 516function byteLength (string, encoding) { 517 if (Buffer.isBuffer(string)) { 518 return string.length 519 } 520 if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
vendor: 5,111 bytes, lines 521-727
521 (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) { 522 return string.byteLength 523 } 524 if (typeof string !== 'string') { 525 string = '' + string 526 } 527 528 var len = string.length 529 if (len === 0) return 0 530 531 // Use a for loop to avoid recursion 532 var loweredCase = false 533 for (;;) { 534 switch (encoding) { 535 case 'ascii': 536 case 'binary': 537 // Deprecated 538 case 'raw': 539 case 'raws': 540 return len 541 case 'utf8': 542 case 'utf-8': 543 case undefined: 544 return utf8ToBytes(string).length 545 case 'ucs2': 546 case 'ucs-2': 547 case 'utf16le': 548 case 'utf-16le': 549 return len * 2 550 case 'hex': 551 return len >>> 1 552 case 'base64': 553 return base64ToBytes(string).length 554 default: 555 if (loweredCase) return utf8ToBytes(string).length // assume utf8 556 encoding = ('' + encoding).toLowerCase() 557 loweredCase = true 558 } 559 } 560} 561Buffer.byteLength = byteLength 562 563function slowToString (encoding, start, end) { 564 var loweredCase = false 565 566 // No need to verify that "this.length <= MAX_UINT32" since it's a read-only 567 // property of a typed array. 568 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) { 574 start = 0 575 } 576 // Return early if start > this.length. Done here to prevent potential uint32 577 // coercion fail below. 578 if (start > this.length) { 579 return '' 580 } 581 582 if (end === undefined || end > this.length) { 583 end = this.length 584 } 585 586 if (end <= 0) { 587 return '' 588 } 589 590 // Force coersion to uint32. This will also coerce falsey/NaN values to 0. 591 end >>>= 0 592 start >>>= 0 593 594 if (end <= start) { 595 return '' 596 } 597 598 if (!encoding) encoding = 'utf8' 599 600 while (true) { 601 switch (encoding) { 602 case 'hex': 603 return hexSlice(this, start, end) 604 605 case 'utf8': 606 case 'utf-8': 607 return utf8Slice(this, start, end) 608 609 case 'ascii': 610 return asciiSlice(this, start, end) 611 612 case 'binary': 613 return binarySlice(this, start, end) 614 615 case 'base64': 616 return base64Slice(this, start, end) 617 618 case 'ucs2': 619 case 'ucs-2': 620 case 'utf16le': 621 case 'utf-16le': 622 return utf16leSlice(this, start, end) 623 624 default: 625 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) 626 encoding = (encoding + '').toLowerCase() 627 loweredCase = true 628 } 629 } 630} 631 632// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect 633// Buffer instances. 634Buffer.prototype._isBuffer = true 635 636function swap (b, n, m) { 637 var i = b[n] 638 b[n] = b[m] 639 b[m] = i 640} 641 642Buffer.prototype.swap16 = function swap16 () { 643 var len = this.length 644 if (len % 2 !== 0) { 645 throw new RangeError('Buffer size must be a multiple of 16-bits') 646 } 647 for (var i = 0; i < len; i += 2) { 648 swap(this, i, i + 1) 649 } 650 return this 651} 652 653Buffer.prototype.swap32 = function swap32 () { 654 var len = this.length 655 if (len % 4 !== 0) { 656 throw new RangeError('Buffer size must be a multiple of 32-bits') 657 } 658 for (var i = 0; i < len; i += 4) { 659 swap(this, i, i + 3) 660 swap(this, i + 1, i + 2) 661 } 662 return this 663} 664 665Buffer.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) 670} 671 672Buffer.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 676} 677 678Buffer.prototype.inspect = function inspect () { 679 var str = '' 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 += ' ... ' 684 } 685 return '<Buffer ' + str + '>' 686} 687 688Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { 689 if (!Buffer.isBuffer(target)) { 690 throw new TypeError('Argument must be a Buffer') 691 } 692 693 if (start === undefined) { 694 start = 0 695 } 696 if (end === undefined) { 697 end = target ? target.length : 0 698 } 699 if (thisStart === undefined) { 700 thisStart = 0 701 } 702 if (thisEnd === undefined) { 703 thisEnd = this.length 704 } 705 706 if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { 707 throw new RangeError('out of range index') 708 } 709 710 if (thisStart >= thisEnd && start >= end) { 711 return 0 712 } 713 if (thisStart >= thisEnd) { 714 return -1 715 } 716 if (start >= end) { 717 return 1 718 } 719 720 start >>>= 0 721 end >>>= 0 722 thisStart >>>= 0 723 thisEnd >>>= 0 724 725 if (this === target) return 0 726 727 var x = thisEnd - thisStart
728 var y = end - start 729 var len = Math.min(x, y) 730 731 var thisCopy = this.slice(thisStart, thisEnd) 732 var targetCopy = target.slice(start, end) 733 734 for (var i = 0; i < len; ++i) { 735 if (thisCopy[i] !== targetCopy[i]) { 736 x = thisCopy[i] 737 y = targetCopy[i] 738 break 739 } 740 } 741 742 if (x < y) return -1 743 if (y < x) return 1 744 return 0 745} 746 747function arrayIndexOf (arr, val, byteOffset, encoding) { 748 var indexSize = 1 749 var arrLength = arr.length 750 var valLength = val.length 751 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) { 757 return -1 758 } 759 indexSize = 2 760 arrLength /= 2 761 valLength /= 2 762 byteOffset /= 2 763 } 764 } 765 766 function read (buf, i) { 767 if (indexSize === 1) { 768 return buf[i] 769 } else { 770 return buf.readUInt16BE(i * indexSize) 771 } 772 } 773 774 var foundIndex = -1 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 779 } else { 780 if (foundIndex !== -1) i -= i - foundIndex 781 foundIndex = -1 782 } 783 } 784 return -1 785} 786 787Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { 788 if (typeof byteOffset === 'string') { 789 encoding = byteOffset 790 byteOffset = 0 791 } else if (byteOffset > 0x7fffffff) { 792 byteOffset = 0x7fffffff 793 } else if (byteOffset < -0x80000000) { 794 byteOffset = -0x80000000 795 } 796 byteOffset >>= 0 797 798 if (this.length === 0) return -1 799 if (byteOffset >= this.length) return -1 800 801 // Negative offsets start from the end of the buffer 802 if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) 803 804 if (typeof val === 'string') { 805 val = Buffer.from(val, encoding) 806 } 807 808 if (Buffer.isBuffer(val)) { 809 // special case: looking for empty string/buffer always fail
vendor: 6,493 bytes, lines 809-1024
809s 810 if (val.length === 0) { 811 return -1 812 } 813 return arrayIndexOf(this, val, byteOffset, encoding) 814 } 815 if (typeof val === 'number') { 816 if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { 817 return Uint8Array.prototype.indexOf.call(this, val, byteOffset) 818 } 819 return arrayIndexOf(this, [ val ], byteOffset, encoding) 820 } 821 822 throw new TypeError('val must be string, number or Buffer') 823} 824 825Buffer.prototype.includes = function includes (val, byteOffset, encoding) { 826 return this.indexOf(val, byteOffset, encoding) !== -1 827} 828 829function hexWrite (buf, string, offset, length) { 830 offset = Number(offset) || 0 831 var remaining = buf.length - offset 832 if (!length) { 833 length = remaining 834 } else { 835 length = Number(length) 836 if (length > remaining) { 837 length = remaining 838 } 839 } 840 841 // must be an even number of digits 842 var strLen = string.length 843 if (strLen % 2 !== 0) throw new Error('Invalid hex string') 844 845 if (length > strLen / 2) { 846 length = strLen / 2 847 } 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 852 } 853 return i 854} 855 856function utf8Write (buf, string, offset, length) { 857 return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) 858} 859 860function asciiWrite (buf, string, offset, length) { 861 return blitBuffer(asciiToBytes(string), buf, offset, length) 862} 863 864function binaryWrite (buf, string, offset, length) { 865 return asciiWrite(buf, string, offset, length) 866} 867 868function base64Write (buf, string, offset, length) { 869 return blitBuffer(base64ToBytes(string), buf, offset, length) 870} 871 872function ucs2Write (buf, string, offset, length) { 873 return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) 874} 875 876Buffer.prototype.write = function write (string, offset, length, encoding) { 877 // Buffer#write(string) 878 if (offset === undefined) { 879 encoding = 'utf8' 880 length = this.length 881 offset = 0 882 // Buffer#write(string, encoding) 883 } else if (length === undefined && typeof offset === 'string') { 884 encoding = offset 885 length = this.length 886 offset = 0 887 // Buffer#write(string, offset[, length][, encoding]) 888 } else if (isFinite(offset)) { 889 offset = offset | 0 890 if (isFinite(length)) { 891 length = length | 0 892 if (encoding === undefined) encoding = 'utf8' 893 } else { 894 encoding = length 895 length = undefined 896 } 897 // legacy write(string, encoding, offset, length) - remove in v0.13 898 } else { 899 throw new Error( 900 'Buffer.write(string, encoding, offset[, length]) is no longer supported' 901 ) 902 } 903 904 var remaining = this.length - offset 905 if (length === undefined || length > remaining) length = remaining 906 907 if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { 908 throw new RangeError('Attempt to write outside buffer bounds') 909 } 910 911 if (!encoding) encoding = 'utf8' 912 913 var loweredCase = false 914 for (;;) { 915 switch (encoding) { 916 case 'hex': 917 return hexWrite(this, string, offset, length) 918 919 case 'utf8': 920 case 'utf-8': 921 return utf8Write(this, string, offset, length) 922 923 case 'ascii': 924 return asciiWrite(this, string, offset, length) 925 926 case 'binary': 927 return binaryWrite(this, string, offset, length) 928 929 case 'base64': 930 // Warning: maxLength not taken into account in base64Write 931 return base64Write(this, string, offset, length) 932 933 case 'ucs2': 934 case 'ucs-2': 935 case 'utf16le': 936 case 'utf-16le': 937 return ucs2Write(this, string, offset, length) 938 939 default: 940 if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) 941 encoding = ('' + encoding).toLowerCase() 942 loweredCase = true 943 } 944 } 945} 946 947Buffer.prototype.toJSON = function toJSON () { 948 return { 949 type: 'Buffer', 950 data: Array.prototype.slice.call(this._arr || this, 0) 951 } 952} 953 954function base64Slice (buf, start, end) { 955 if (start === 0 && end === buf.length) { 956 return base64.fromByteArray(buf) 957 } else { 958 return base64.fromByteArray(buf.slice(start, end)) 959 } 960} 961 962function utf8Slice (buf, start, end) { 963 end = Math.min(buf.length, end) 964 var res = [] 965 966 var i = start 967 while (i < end) { 968 var firstByte = buf[i] 969 var codePoint = null 970 var bytesPerSequence = (firstByte > 0xEF) ? 4 971 : (firstByte > 0xDF) ? 3 972 : (firstByte > 0xBF) ? 2 973 : 1 974 975 if (i + bytesPerSequence <= end) { 976 var secondByte, thirdByte, fourthByte, tempCodePoint 977 978 switch (bytesPerSequence) { 979 case 1: 980 if (firstByte < 0x80) { 981 codePoint = firstByte 982 } 983 break 984 case 2: 985 secondByte = buf[i + 1] 986 if ((secondByte & 0xC0) === 0x80) { 987 tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) 988 if (tempCodePoint > 0x7F) { 989 codePoint = tempCodePoint 990 } 991 } 992 break 993 case 3: 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 1000 } 1001 } 1002 break 1003 case 4: 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 1011 } 1012 } 1013 } 1014 } 1015 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 1019 codePoint = 0xFFFD 1020 bytesPerSequence = 1 1021 } else if (codePoint > 0xFFFF) { 1022 // encode to utf16 (surrogate pair dance) 1023 codePoint -= 0x10000 1024 res.push(codePoint >>>
vendor: 2,544 bytes, lines 1024-1138
1024 10 & 0x3FF | 0xD800) 1025 codePoint = 0xDC00 | codePoint & 0x3FF 1026 } 1027 1028 res.push(codePoint) 1029 i += bytesPerSequence 1030 } 1031 1032 return decodeCodePointsArray(res) 1033} 1034 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 1038var MAX_ARGUMENTS_LENGTH = 0x1000 1039 1040function decodeCodePointsArray (codePoints) { 1041 var len = codePoints.length 1042 if (len <= MAX_ARGUMENTS_LENGTH) { 1043 return String.fromCharCode.apply(String, codePoints) // avoid extra slice() 1044 } 1045 1046 // Decode in chunks to avoid "call stack size exceeded". 1047 var res = '' 1048 var i = 0 1049 while (i < len) { 1050 res += String.fromCharCode.apply( 1051 String, 1052 codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) 1053 ) 1054 } 1055 return res 1056} 1057 1058function asciiSlice (buf, start, end) { 1059 var ret = '' 1060 end = Math.min(buf.length, end) 1061 1062 for (var i = start; i < end; i++) { 1063 ret += String.fromCharCode(buf[i] & 0x7F) 1064 } 1065 return ret 1066} 1067 1068function binarySlice (buf, start, end) { 1069 var ret = '' 1070 end = Math.min(buf.length, end) 1071 1072 for (var i = start; i < end; i++) { 1073 ret += String.fromCharCode(buf[i]) 1074 } 1075 return ret 1076} 1077 1078function hexSlice (buf, start, end) { 1079 var len = buf.length 1080 1081 if (!start || start < 0) start = 0 1082 if (!end || end < 0 || end > len) end = len 1083 1084 var out = '' 1085 for (var i = start; i < end; i++) { 1086 out += toHex(buf[i]) 1087 } 1088 return out 1089} 1090 1091function utf16leSlice (buf, start, end) { 1092 var bytes = buf.slice(start, end) 1093 var res = '' 1094 for (var i = 0; i < bytes.length; i += 2) { 1095 res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) 1096 } 1097 return res 1098} 1099 1100Buffer.prototype.slice = function slice (start, end) { 1101 var len = this.length 1102 start = ~~start 1103 end = end === undefined ? len : ~~end 1104 1105 if (start < 0) { 1106 start += len 1107 if (start < 0) start = 0 1108 } else if (start > len) { 1109 start = len 1110 } 1111 1112 if (end < 0) { 1113 end += len 1114 if (end < 0) end = 0 1115 } else if (end > len) { 1116 end = len 1117 } 1118 1119 if (end < start) end = start 1120 1121 var newBuf 1122 if (Buffer.TYPED_ARRAY_SUPPORT) { 1123 newBuf = this.subarray(start, end) 1124 newBuf.__proto__ = Buffer.prototype 1125 } else { 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] 1130 } 1131 } 1132 1133 return newBuf 1134} 1135 1136/* 1137 * Need to make sure that buffer isn't trying to write out of bounds. 1138 */
vendor: 7,760 bytes, lines 1139-1389
1139function 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') 1142} 1143 1144Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { 1145 offset = offset | 0 1146 byteLength = byteLength | 0 1147 if (!noAssert) checkOffset(offset, byteLength, this.length) 1148 1149 var val = this[offset] 1150 var mul = 1 1151 var i = 0 1152 while (++i < byteLength && (mul *= 0x100)) { 1153 val += this[offset + i] * mul 1154 } 1155 1156 return val 1157} 1158 1159Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { 1160 offset = offset | 0 1161 byteLength = byteLength | 0 1162 if (!noAssert) { 1163 checkOffset(offset, byteLength, this.length) 1164 } 1165 1166 var val = this[offset + --byteLength] 1167 var mul = 1 1168 while (byteLength > 0 && (mul *= 0x100)) { 1169 val += this[offset + --byteLength] * mul 1170 } 1171 1172 return val 1173} 1174 1175Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { 1176 if (!noAssert) checkOffset(offset, 1, this.length) 1177 return this[offset] 1178} 1179 1180Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { 1181 if (!noAssert) checkOffset(offset, 2, this.length) 1182 return this[offset] | (this[offset + 1] << 8) 1183} 1184 1185Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { 1186 if (!noAssert) checkOffset(offset, 2, this.length) 1187 return (this[offset] << 8) | this[offset + 1] 1188} 1189 1190Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { 1191 if (!noAssert) checkOffset(offset, 4, this.length) 1192 1193 return ((this[offset]) | 1194 (this[offset + 1] << 8) | 1195 (this[offset + 2] << 16)) + 1196 (this[offset + 3] * 0x1000000) 1197} 1198 1199Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { 1200 if (!noAssert) checkOffset(offset, 4, this.length) 1201 1202 return (this[offset] * 0x1000000) + 1203 ((this[offset + 1] << 16) | 1204 (this[offset + 2] << 8) | 1205 this[offset + 3]) 1206} 1207 1208Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { 1209 offset = offset | 0 1210 byteLength = byteLength | 0 1211 if (!noAssert) checkOffset(offset, byteLength, this.length) 1212 1213 var val = this[offset] 1214 var mul = 1 1215 var i = 0 1216 while (++i < byteLength && (mul *= 0x100)) { 1217 val += this[offset + i] * mul 1218 } 1219 mul *= 0x80 1220 1221 if (val >= mul) val -= Math.pow(2, 8 * byteLength) 1222 1223 return val 1224} 1225 1226Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { 1227 offset = offset | 0 1228 byteLength = byteLength | 0 1229 if (!noAssert) checkOffset(offset, byteLength, this.length) 1230 1231 var i = byteLength 1232 var mul = 1 1233 var val = this[offset + --i] 1234 while (i > 0 && (mul *= 0x100)) { 1235 val += this[offset + --i] * mul 1236 } 1237 mul *= 0x80 1238 1239 if (val >= mul) val -= Math.pow(2, 8 * byteLength) 1240 1241 return val 1242} 1243 1244Buffer.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) 1248} 1249 1250Buffer.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 1254} 1255 1256Buffer.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 1260} 1261 1262Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { 1263 if (!noAssert) checkOffset(offset, 4, this.length) 1264 1265 return (this[offset]) | 1266 (this[offset + 1] << 8) | 1267 (this[offset + 2] << 16) | 1268 (this[offset + 3] << 24) 1269} 1270 1271Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { 1272 if (!noAssert) checkOffset(offset, 4, this.length) 1273 1274 return (this[offset] << 24) | 1275 (this[offset + 1] << 16) | 1276 (this[offset + 2] << 8) | 1277 (this[offset + 3]) 1278} 1279 1280Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { 1281 if (!noAssert) checkOffset(offset, 4, this.length) 1282 return ieee754.read(this, offset, true, 23, 4) 1283} 1284 1285Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { 1286 if (!noAssert) checkOffset(offset, 4, this.length) 1287 return ieee754.read(this, offset, false, 23, 4) 1288} 1289 1290Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { 1291 if (!noAssert) checkOffset(offset, 8, this.length) 1292 return ieee754.read(this, offset, true, 52, 8) 1293} 1294 1295Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { 1296 if (!noAssert) checkOffset(offset, 8, this.length) 1297 return ieee754.read(this, offset, false, 52, 8) 1298} 1299 1300function 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') 1304} 1305 1306Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { 1307 value = +value 1308 offset = offset | 0 1309 byteLength = byteLength | 0 1310 if (!noAssert) { 1311 var maxBytes = Math.pow(2, 8 * byteLength) - 1 1312 checkInt(this, value, offset, byteLength, maxBytes, 0) 1313 } 1314 1315 var mul = 1 1316 var i = 0 1317 this[offset] = value & 0xFF 1318 while (++i < byteLength && (mul *= 0x100)) { 1319 this[offset + i] = (value / mul) & 0xFF 1320 } 1321 1322 return offset + byteLength 1323} 1324 1325Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { 1326 value = +value 1327 offset = offset | 0 1328 byteLength = byteLength | 0 1329 if (!noAssert) { 1330 var maxBytes = Math.pow(2, 8 * byteLength) - 1 1331 checkInt(this, value, offset, byteLength, maxBytes, 0) 1332 } 1333 1334 var i = byteLength - 1 1335 var mul = 1 1336 this[offset + i] = value & 0xFF 1337 while (--i >= 0 && (mul *= 0x100)) { 1338 this[offset + i] = (value / mul) & 0xFF 1339 } 1340 1341 return offset + byteLength 1342} 1343 1344Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { 1345 value = +value 1346 offset = offset | 0 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) 1350 return offset + 1 1351} 1352 1353function 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 1358 } 1359} 1360 1361Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { 1362 value = +value 1363 offset = offset | 0 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) 1368 } else { 1369 objectWriteUInt16(this, value, offset, true) 1370 } 1371 return offset + 2 1372} 1373 1374Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { 1375 value = +value 1376 offset = offset | 0 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) 1381 } else { 1382 objectWriteUInt16(this, value, offset, false) 1383 } 1384 return offset + 2 1385} 1386 1387function 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++
vendor: 6,798 bytes, lines 1389-1604
1389) { 1390 buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff 1391 } 1392} 1393 1394Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { 1395 value = +value 1396 offset = offset | 0 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) 1403 } else { 1404 objectWriteUInt32(this, value, offset, true) 1405 } 1406 return offset + 4 1407} 1408 1409Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { 1410 value = +value 1411 offset = offset | 0 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) 1418 } else { 1419 objectWriteUInt32(this, value, offset, false) 1420 } 1421 return offset + 4 1422} 1423 1424Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { 1425 value = +value 1426 offset = offset | 0 1427 if (!noAssert) { 1428 var limit = Math.pow(2, 8 * byteLength - 1) 1429 1430 checkInt(this, value, offset, byteLength, limit - 1, -limit) 1431 } 1432 1433 var i = 0 1434 var mul = 1 1435 var sub = 0 1436 this[offset] = value & 0xFF 1437 while (++i < byteLength && (mul *= 0x100)) { 1438 if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { 1439 sub = 1 1440 } 1441 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF 1442 } 1443 1444 return offset + byteLength 1445} 1446 1447Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { 1448 value = +value 1449 offset = offset | 0 1450 if (!noAssert) { 1451 var limit = Math.pow(2, 8 * byteLength - 1) 1452 1453 checkInt(this, value, offset, byteLength, limit - 1, -limit) 1454 } 1455 1456 var i = byteLength - 1 1457 var mul = 1 1458 var sub = 0 1459 this[offset + i] = value & 0xFF 1460 while (--i >= 0 && (mul *= 0x100)) { 1461 if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { 1462 sub = 1 1463 } 1464 this[offset + i] = ((value / mul) >> 0) - sub & 0xFF 1465 } 1466 1467 return offset + byteLength 1468} 1469 1470Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { 1471 value = +value 1472 offset = offset | 0 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) 1477 return offset + 1 1478} 1479 1480Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { 1481 value = +value 1482 offset = offset | 0 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) 1487 } else { 1488 objectWriteUInt16(this, value, offset, true) 1489 } 1490 return offset + 2 1491} 1492 1493Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { 1494 value = +value 1495 offset = offset | 0 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) 1500 } else { 1501 objectWriteUInt16(this, value, offset, false) 1502 } 1503 return offset + 2 1504} 1505 1506Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { 1507 value = +value 1508 offset = offset | 0 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) 1515 } else { 1516 objectWriteUInt32(this, value, offset, true) 1517 } 1518 return offset + 4 1519} 1520 1521Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { 1522 value = +value 1523 offset = offset | 0 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) 1531 } else { 1532 objectWriteUInt32(this, value, offset, false) 1533 } 1534 return offset + 4 1535} 1536 1537function 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') 1540} 1541 1542function writeFloat (buf, value, offset, littleEndian, noAssert) { 1543 if (!noAssert) { 1544 checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) 1545 } 1546 ieee754.write(buf, value, offset, littleEndian, 23, 4) 1547 return offset + 4 1548} 1549 1550Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { 1551 return writeFloat(this, value, offset, true, noAssert) 1552} 1553 1554Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { 1555 return writeFloat(this, value, offset, false, noAssert) 1556} 1557 1558function writeDouble (buf, value, offset, littleEndian, noAssert) { 1559 if (!noAssert) { 1560 checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) 1561 } 1562 ieee754.write(buf, value, offset, littleEndian, 52, 8) 1563 return offset + 8 1564} 1565 1566Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { 1567 return writeDouble(this, value, offset, true, noAssert) 1568} 1569 1570Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { 1571 return writeDouble(this, value, offset, false, noAssert) 1572} 1573 1574// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) 1575Buffer.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 1581 1582 // Copy 0 bytes; we're done 1583 if (end === start) return 0 1584 if (target.length === 0 || this.length === 0) return 0 1585 1586 // Fatal error conditions 1587 if (targetStart < 0) { 1588 throw new RangeError('targetStart out of bounds') 1589 } 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') 1592 1593 // Are we oob? 1594 if (end > this.length) end = this.length 1595 if (target.length - targetStart < end - start) { 1596 end = target.length - targetStart + start 1597 } 1598 1599 var len = end - start 1600 var i 1601 1602 if (this === target && start < targetStart && targetStart < end) { 1603 // descending copy from end 1604 for (i = len - 1; i >= 0;
vendor: 5,075 bytes, lines 1604-1796
1604i--) { 1605 target[i + targetStart] = this[i + start] 1606 } 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] 1611 } 1612 } else { 1613 Uint8Array.prototype.set.call( 1614 target, 1615 this.subarray(start, start + len), 1616 targetStart 1617 ) 1618 } 1619 1620 return len 1621} 1622 1623// Usage: 1624// buffer.fill(number[, offset[, end]]) 1625// buffer.fill(buffer[, offset[, end]]) 1626// buffer.fill(string[, offset[, end]][, encoding]) 1627Buffer.prototype.fill = function fill (val, start, end, encoding) { 1628 // Handle string cases: 1629 if (typeof val === 'string') { 1630 if (typeof start === 'string') { 1631 encoding = start 1632 start = 0 1633 end = this.length 1634 } else if (typeof end === 'string') { 1635 encoding = end 1636 end = this.length 1637 } 1638 if (val.length === 1) { 1639 var code = val.charCodeAt(0) 1640 if (code < 256) { 1641 val = code 1642 } 1643 } 1644 if (encoding !== undefined && typeof encoding !== 'string') { 1645 throw new TypeError('encoding must be a string') 1646 } 1647 if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { 1648 throw new TypeError('Unknown encoding: ' + encoding) 1649 } 1650 } else if (typeof val === 'number') { 1651 val = val & 255 1652 } 1653 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') 1657 } 1658 1659 if (end <= start) { 1660 return this 1661 } 1662 1663 start = start >>> 0 1664 end = end === undefined ? this.length : end >>> 0 1665 1666 if (!val) val = 0 1667 1668 var i 1669 if (typeof val === 'number') { 1670 for (i = start; i < end; i++) { 1671 this[i] = val 1672 } 1673 } else { 1674 var bytes = Buffer.isBuffer(val) 1675 ? 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] 1680 } 1681 } 1682 1683 return this 1684} 1685 1686// HELPER FUNCTIONS 1687// ================ 1688 1689var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g 1690 1691function 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) { 1698 str = str + '=' 1699 } 1700 return str 1701} 1702 1703function stringtrim (str) { 1704 if (str.trim) return str.trim() 1705 return str.replace(/^\s+|\s+$/g, '') 1706} 1707 1708function toHex (n) { 1709 if (n < 16) return '0' + n.toString(16) 1710 return n.toString(16) 1711} 1712 1713function utf8ToBytes (string, units) { 1714 units = units || Infinity 1715 var codePoint 1716 var length = string.length 1717 var leadSurrogate = null 1718 var bytes = [] 1719 1720 for (var i = 0; i < length; i++) { 1721 codePoint = string.charCodeAt(i) 1722 1723 // is surrogate component 1724 if (codePoint > 0xD7FF && codePoint < 0xE000) { 1725 // last char was a lead 1726 if (!leadSurrogate) { 1727 // no lead yet 1728 if (codePoint > 0xDBFF) { 1729 // unexpected trail 1730 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) 1731 continue 1732 } else if (i + 1 === length) { 1733 // unpaired lead 1734 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) 1735 continue 1736 } 1737 1738 // valid lead 1739 leadSurrogate = codePoint 1740 1741 continue 1742 } 1743 1744 // 2 leads in a row 1745 if (codePoint < 0xDC00) { 1746 if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) 1747 leadSurrogate = codePoint 1748 continue 1749 } 1750 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) 1756 } 1757 1758 leadSurrogate = null 1759 1760 // encode utf8 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 1766 bytes.push( 1767 codePoint >> 0x6 | 0xC0, 1768 codePoint & 0x3F | 0x80 1769 ) 1770 } else if (codePoint < 0x10000) { 1771 if ((units -= 3) < 0) break 1772 bytes.push( 1773 codePoint >> 0xC | 0xE0, 1774 codePoint >> 0x6 & 0x3F | 0x80, 1775 codePoint & 0x3F | 0x80 1776 ) 1777 } else if (codePoint < 0x110000) { 1778 if ((units -= 4) < 0) break 1779 bytes.push( 1780 codePoint >> 0x12 | 0xF0, 1781 codePoint >> 0xC & 0x3F | 0x80, 1782 codePoint >> 0x6 & 0x3F | 0x80, 1783 codePoint & 0x3F | 0x80 1784 ) 1785 } else { 1786 throw new Error('Invalid code point') 1787 } 1788 } 1789 1790 return bytes 1791} 1792 1793function asciiToBytes (str) { 1794 var byteArray = [] 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) 1798 } 1799 return byteArray 1800} 1801 1802function utf16leToBytes (str, units) { 1803 var c, hi, lo 1804 var byteArray = [] 1805 for (var i = 0; i < str.length; i++) { 1806 if ((units -= 2) < 0) break 1807 1808 c = str.charCodeAt(i) 1809 hi = c >> 8 1810 lo = c % 256 1811 byteArray.push(lo) 1812 byteArray.push(hi) 1813 } 1814 1815 return byteArray 1816} 1817 1818function base64ToBytes (str) { 1819 return base64.toByteArray(base64clean(str)) 1820} 1821 1822function 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] 1826 } 1827 return i 1828} 1829 1830function isnan (val) { 1831 return val !== val // eslint-disable-line no-self-compare 1832} 1833 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) 1837"use strict"; 1838 1839 1840// Requires 1841var Typo = require("typo-js"); 1842 1843 1844// Create function 1845function CodeMirrorSpellChecker(options) { 1846 // Initialize 1847 options = options || {}; 1848 1849 1850 // Verify 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`"); 1853 return; 1854 } 1855 1856 1857 // Because some browsers don't support this functionality yet 1858 if(!String.prototype.includes) { 1859 String.prototype.includes = function() { 1860 "use strict"; 1861 return String.prototype.indexOf.apply(this, arguments) !== -1; 1862 }; 1863 } 1864 1865 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++; 1877 1878 if(CodeMirrorSpellChecker.num_loaded == 2) { 1879 CodeMirrorSpellChecker.typo = new Typo("en_US", CodeMirrorSpellChecker.aff_data, CodeMirrorSpellChecker.dic_data, { 1880 platform: "any" 1881 }); 1882 } 1883 } 1884 }; 1885 xhr_aff.send(null); 1886 } 1887 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++; 1896 1897 if(CodeMirrorSpellChecker.num_loaded == 2) { 1898 CodeMirrorSpellChecker.typo = new Typo("en_US", CodeMirrorSpellChecker.aff_data, CodeMirrorSpellChecker.dic_data, { 1899 platform: "any" 1900 }); 1901 } 1902 } 1903 }; 1904 xhr_dic.send(null); 1905 } 1906 1907 1908 // Define what separates a word 1909 var rx_word = "!\"#$%&()*+,-./:;<=>?@[\\]^_`{|}~ "; 1910 1911 1912 // Create the overlay and such 1913 var overlay = { 1914 token: function(stream) { 1915 var ch = stream.peek(); 1916 var word = ""; 1917 1918 if(rx_word.includes(ch)) { 1919 stream.next(); 1920 return null; 1921 } 1922 1923 while((ch = stream.peek()) != null && !rx_word.includes(ch)) { 1924 word += ch; 1925 stream.next(); 1926 } 1927 1928 if(CodeMirrorSpellChecker.typo && !CodeMirrorSpellChecker.typo.check(word)) 1929 return "spell-error"; // CSS class: cm-spell-error 1930 1931 return null; 1932 } 1933 }; 1934 1935 var mode = options.codeMirrorInstance.getMode( 1936 config, config.backdrop || "text/plain" 1937 ); 1938 1939 return options.codeMirrorInstance.overlayMode(mode, overlay, true); 1940 }); 1941} 1942 1943 1944// Initialize data globally to reduce memory consumption 1945CodeMirrorSpellChecker.num_loaded = 0; 1946CodeMirrorSpellChecker.aff_loading = false; 1947CodeMirrorSpellChecker.dic_loading = false; 1948CodeMirrorSpellChecker.aff_data = ""; 1949CodeMirrorSpellChecker.dic_data = ""; 1950CodeMirrorSpellChecker.typo; 1951 1952 1953// Export 1954module.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 1958 1959(function(mod) { 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 1965 mod(CodeMirror); 1966})(function(CodeMirror) { 1967 "use strict"; 1968 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); 1973 else setNormal(cm); 1974 }); 1975 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"; 1984 cm.refresh();
1985 } 1986 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); 1994 cm.refresh(); 1995 } 1996}); 1997 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 2001 2002(function(mod) { 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 2008 mod(CodeMirror); 2009})(function(CodeMirror) { 2010 CodeMirror.defineOption("placeholder", "", function(cm, val, old) { 2011 var prev = old && old != CodeMirror.Init; 2012 if (val && !prev) { 2013 cm.on("blur", onBlur); 2014 cm.on("change", onChange); 2015 cm.on("swapDoc", onChange); 2016 onChange(cm); 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", ""); 2024 } 2025 2026 if (val && !cm.hasFocus()) onBlur(cm); 2027 }); 2028 2029 function clearPlaceholder(cm) { 2030 if (cm.state.placeholder) { 2031 cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); 2032 cm.state.placeholder = null; 2033 } 2034 } 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); 2044 } 2045 2046 function onBlur(cm) { 2047 if (isEmpty(cm)) setPlaceholder(cm); 2048 } 2049 function onChange(cm) { 2050 var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); 2051 wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); 2052 2053 if (empty) setPlaceholder(cm); 2054 else clearPlaceholder(cm); 2055 } 2056 2057 function isEmpty(cm) { 2058 return (cm.lineCount() === 1) && (cm.getLine(0) === ""); 2059 } 2060}); 2061 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 2065 2066(function(mod) { 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 2072 mod(CodeMirror); 2073})(function(CodeMirror) { 2074 "use strict"; 2075 2076 var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))(\s*)/, 2077 emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)[.)])(\s*)$/, 2078 unorderedListRE = /[*+-]\s/; 2079 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; 2088 2089 var line = cm.getLine(pos.line), match = listRE.exec(line); 2090 if (!ranges[i].empty() || (!inList && !inQuote) || !match) { 2091 cm.execCommand("newlineAndIndent"); 2092 return; 2093 } 2094 if (emptyListRE.test(line)) { 2095 cm.replaceRange("", { 2096 line: pos.line, ch: 0 2097 }, { 2098 line: pos.line, ch: pos.ch + 1 2099 }); 2100 replacements[i] = "\n"; 2101 } else { 2102 var indent = match[1], after = match[5]; 2103 var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0 2104 ? match[2] 2105 : (parseInt(match[3], 10) + 1) + match[4]; 2106 2107 replacements[i] = "\n" + indent + bullet + after; 2108 } 2109 } 2110 2111 cm.replaceSelections(replacements); 2112 }; 2113}); 2114 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 2118 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 2126// combined. 2127 2128(function(mod) { 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 2134 mod(CodeMirror); 2135})(function(CodeMirror) { 2136"use strict"; 2137
2138CodeMirror.overlayMode = function(base, overlay, combine) { 2139 return { 2140 startState: function() { 2141 return { 2142 base: CodeMirror.startState(base), 2143 overlay: CodeMirror.startState(overlay), 2144 basePos: 0, baseCur: null, 2145 overlayPos: 0, overlayCur: null, 2146 streamSeen: null 2147 }; 2148 }, 2149 copyState: function(state) { 2150 return { 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 2155 }; 2156 }, 2157 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; 2163 } 2164 2165 if (stream.start == state.basePos) { 2166 state.baseCur = base.token(stream, state.base); 2167 state.basePos = stream.pos; 2168 } 2169 if (stream.start == state.overlayPos) { 2170 stream.pos = stream.start; 2171 state.overlayCur = overlay.token(stream, state.overlay); 2172 state.overlayPos = stream.pos; 2173 } 2174 stream.pos = Math.min(state.basePos, state.overlayPos); 2175 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; 2184 }, 2185 2186 indent: base.indent && function(state, textAfter) { 2187 return base.indent(state.base, textAfter); 2188 }, 2189 electricChars: base.electricChars, 2190 2191 innerMode: function(state) { return {state: state.base, mode: base}; }, 2192 2193 blankLine: function(state) { 2194 if (base.blankLine) base.blankLine(state.base); 2195 if (overlay.blankLine) overlay.blankLine(state.overlay); 2196 } 2197 }; 2198}; 2199 2200}); 2201 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 2205 2206// Because sometimes you need to mark the selected *text*. 2207// 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. 2211 2212(function(mod) { 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 2218 mod(CodeMirror); 2219})(function(CodeMirror) { 2220 "use strict"; 2221 2222 CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { 2223 var prev = old && old != CodeMirror.Init; 2224 if (val && !prev) { 2225 cm.state.markedSelection = []; 2226 cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; 2227 reset(cm); 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); 2233 clear(cm); 2234 cm.state.markedSelection = cm.state.markedSelectionStyle = null; 2235 } 2236 }); 2237 2238 function onCursorActivity(cm) { 2239 cm.operation(function() { update(cm); }); 2240 } 2241 2242 function onChange(cm) { 2243 if (cm.state.markedSelection.length) 2244 cm.operation(function() { clear(cm); }); 2245 } 2246 2247 var CHUNK_SIZE = 8; 2248 var Pos = CodeMirror.Pos; 2249 var cmp = CodeMirror.cmpPos; 2250 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); 2262 if (atEnd) break; 2263 line = endLine; 2264 } 2265 } 2266 2267 function clear(cm) { 2268 var array = cm.state.markedSelection; 2269 for (var i = 0; i < array.length; ++i) array[i].clear(); 2270 array.length = 0; 2271 } 2272 2273 function reset(cm) { 2274 clear(cm); 2275 var ranges = cm.listSelections(); 2276 for (var i = 0; i < ranges.length; i++) 2277 coverRange(cm, ranges[i].from(), ranges[i].to()); 2278 } 2279 2280 function update(cm) { 2281 if (!cm.somethingSelected()) return clear(cm); 2282 if (cm.listSelections().length > 1) return reset(cm); 2283 2284 var from = cm.getCursor("start"), to = cm.getCursor("end"); 2285 2286 var array = cm.state.markedSelection; 2287 if (!array.length) return coverRange(cm, from, to); 2288 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) 2292 return reset(cm); 2293 2294 while (cmp(from, coverStart.from) > 0) { 2295 array.shift().clear(); 2296 coverStart = array[0].find(); 2297 } 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); 2302 } else { 2303 coverRange(cm, from, coverStart.from, 0); 2304 } 2305 } 2306 2307 while (cmp(to, coverEnd.to) < 0) { 2308 array.pop().clear(); 2309 coverEnd = array[array.length - 1].find(); 2310 } 2311 if (cmp(to, coverEnd.to) > 0) {
vendor: 364,745 bytes, lines 2312-11246
2312 if (to.line - coverEnd.from.line < CHUNK_SIZE) { 2313 array.pop().clear(); 2314 coverRange(cm, coverEnd.from, to); 2315 } else { 2316 coverRange(cm, coverEnd.to, to); 2317 } 2318 } 2319 } 2320}); 2321 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 2325 2326// This is CodeMirror (http://codemirror.net), a code editor 2327// implemented in JavaScript on top of the browser's DOM. 2328// 2329// You can find some technical background for some of the code below 2330// at http://marijnhaverbeke.nl/blog/#cm-internals . 2331 2332(function(mod) { 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(); 2339})(function() { 2340 "use strict"; 2341 2342 // BROWSER SNIFFING 2343 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; 2348 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); 2361 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); 2368 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); 2375 2376 // Optimize some code when these features are not used. 2377 var sawReadOnlySpans = false, sawCollapsedSpans = false; 2378 2379 // EDITOR CONSTRUCTOR 2380 2381 // A CodeMirror instance represents an editor. This is the object 2382 // that user code is usually dealing with. 2383 2384 function CodeMirror(place, options) { 2385 if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); 2386 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); 2391 2392 var doc = options.value; 2393 if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator); 2394 this.doc = doc; 2395 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); 2400 themeChanged(this); 2401 if (options.lineWrapping) 2402 this.display.wrapper.className += " CodeMirror-wrap"; 2403 if (options.autofocus && !mobile) display.input.focus(); 2404 initScrollbars(this); 2405 2406 this.state = { 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 2410 overwrite: false, 2411 delayingBlurEvent: false, 2412 focused: 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 2419 specialChars: null 2420 }; 2421 2422 var cm = this; 2423 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); 2427 2428 registerEventHandlers(this); 2429 ensureGlobalHandlers(); 2430 2431 startOperation(this); 2432 this.curOp.forceUpdate = true; 2433 attachDoc(this, doc); 2434 2435 if ((options.autofocus && !mobile) || cm.hasFocus()) 2436 setTimeout(bind(onFocus, this), 20); 2437 else 2438 onBlur(this); 2439 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); 2445 endOperation(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"; 2451 } 2452 2453 // DISPLAY CONSTRUCTOR 2454 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. 2458 2459 function Display(place, doc, input) { 2460 var d = this; 2461 this.input = input; 2462 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"); 2499 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; 2503 2504 if (place) { 2505 if (place.appendChild) place.appendChild(d.wrapper); 2506 else place(d.wrapper); 2507 } 2508 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. 2513 d.view = []; 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 2519 d.viewOffset = 0; 2520 d.lastWrapHeight = d.lastWrapWidth = 0; 2521 d.updateLineNumbers = null; 2522 2523 d.nativeBarWidth = d.barHeight = d.barWidth = 0; 2524 d.scrollbarsClipped = false; 2525 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 2531 // this is false. 2532 d.alignWidgets = false; 2533 2534 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; 2535 2536 // Tracks the maximum line length so that the horizontal scrollbar 2537 // can be kept static when scrolling. 2538 d.maxLine = null; 2539 d.maxLineLength = 0; 2540 d.maxLineChanged = false; 2541 2542 // Used for measuring wheel scrolling granularity 2543 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; 2544 2545 // True when shift is held down. 2546 d.shift = false; 2547 2548 // Used to track whether anything happened since the context menu 2549 // was opened. 2550 d.selForContextMenu = null; 2551 2552 d.activeTouch = null; 2553 2554 input.init(d); 2555 } 2556 2557 // STATE UPDATES 2558 2559 // Used to get the editor into a consistent state again when options change. 2560 2561 function loadMode(cm) { 2562 cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); 2563 resetModeState(cm); 2564 } 2565 2566 function resetModeState(cm) { 2567 cm.doc.iter(function(line) { 2568 if (line.stateAfter) line.stateAfter = null; 2569 if (line.styles) line.styles = null; 2570 }); 2571 cm.doc.frontier = cm.doc.first; 2572 startWorker(cm, 100); 2573 cm.state.modeGen++; 2574 if (cm.curOp) regChange(cm); 2575 } 2576 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; 2582 } else { 2583 rmClass(cm.display.wrapper, "CodeMirror-wrap"); 2584 findMaxLine(cm); 2585 } 2586 estimateLineHeights(cm); 2587 regChange(cm); 2588 clearCaches(cm); 2589 setTimeout(function(){updateScrollbars(cm);}, 100); 2590 } 2591 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; 2600 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; 2604 } 2605 2606 if (wrapping) 2607 return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th; 2608 else 2609 return widgetsHeight + th; 2610 }; 2611 } 2612 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); 2618 }); 2619 } 2620 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-"); 2624 clearCaches(cm); 2625 } 2626 2627 function guttersChanged(cm) { 2628 updateGutters(cm); 2629 regChange(cm); 2630 setTimeout(function(){alignHorizontally(cm);}, 20); 2631 } 2632 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"; 2644 } 2645 } 2646 gutters.style.display = i ? "" : "none"; 2647 updateGutterSpace(cm); 2648 } 2649 2650 function updateGutterSpace(cm) { 2651 var width = cm.display.gutters.offsetWidth; 2652 cm.display.sizer.style.marginLeft = width + "px"; 2653 } 2654 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; 2665 } 2666 cur = line; 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; 2672 } 2673 return len; 2674 } 2675 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; 2686 d.maxLine = line; 2687 } 2688 }); 2689 } 2690 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); 2700 } 2701 } 2702 2703 // SCROLLBARS 2704 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)); 2710 return { 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, 2716 docHeight: docH, 2717 scrollHeight: docH + scrollGap(cm) + d.barHeight, 2718 nativeBarWidth: d.nativeBarWidth, 2719 gutterWidth: gutterW 2720 }; 2721 } 2722 2723 function NativeScrollbars(place, scroll, cm) { 2724 this.cm = 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); 2728 2729 on(vert, "scroll", function() { 2730 if (vert.clientHeight) scroll(vert.scrollTop, "vertical"); 2731 }); 2732 on(horiz, "scroll", function() { 2733 if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal"); 2734 }); 2735 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"; 2739 } 2740 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; 2746 2747 if (needsV) { 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"; 2754 } else { 2755 this.vert.style.display = ""; 2756 this.vert.firstChild.style.height = "0"; 2757 } 2758 2759 if (needsH) { 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"; 2766 } else { 2767 this.horiz.style.display = ""; 2768 this.horiz.firstChild.style.width = "0"; 2769 } 2770 2771 if (!this.checkedZeroWidth && measure.clientHeight > 0) { 2772 if (sWidth == 0) this.zeroWidthHack(); 2773 this.checkedZeroWidth = true; 2774 } 2775 2776 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}; 2777 }, 2778 setScrollLeft: function(pos) { 2779 if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos; 2780 if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz); 2781 }, 2782 setScrollTop: function(pos) { 2783 if (this.vert.scrollTop != pos) this.vert.scrollTop = pos; 2784 if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert); 2785 }, 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; 2792 }, 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); 2806 } 2807 delay.set(1000, maybeDisable); 2808 }, 2809 clear: function() { 2810 var parent = this.horiz.parentNode; 2811 parent.removeChild(this.horiz); 2812 parent.removeChild(this.vert); 2813 } 2814 }, NativeScrollbars.prototype); 2815 2816 function NullScrollbars() {} 2817 2818 NullScrollbars.prototype = copyObj({ 2819 update: function() { return {bottom: 0, right: 0}; }, 2820 setScrollLeft: function() {}, 2821 setScrollTop: function() {}, 2822 clear: function() {} 2823 }, NullScrollbars.prototype); 2824 2825 CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; 2826 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); 2832 } 2833 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); 2839 }); 2840 node.setAttribute("cm-not-content", "true"); 2841 }, function(pos, axis) { 2842 if (axis == "horizontal") setScrollLeft(cm, pos); 2843 else setScrollTop(cm, pos); 2844 }, cm); 2845 if (cm.display.scrollbars.addClass) 2846 addClass(cm.display.wrapper, cm.display.scrollbars.addClass); 2847 } 2848 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; 2858 } 2859 } 2860 2861 // Re-synchronize the fake scrollbars with the actual size of the 2862 // content. 2863 function updateScrollbarsInner(cm, measure) { 2864 var d = cm.display; 2865 var sizes = d.scrollbars.update(measure); 2866 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" 2870 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 = ""; 2881 } 2882 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; 2890 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) { 2897 from = ensureFrom; 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); 2901 to = ensureTo; 2902 } 2903 } 2904 return {from: from, to: Math.max(to, from + 1)}; 2905 } 2906 2907 // LINE NUMBERS 2908 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; 2922 } 2923 if (cm.options.fixedGutter) 2924 display.gutters.style.left = (comp + gutterW) + "px"; 2925 } 2926 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 2929 // is needed. 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); 2943 return true; 2944 } 2945 return false; 2946 } 2947 2948 function lineNumberFor(options, i) { 2949 return String(options.lineNumberFormatter(i + options.firstLineNumber)); 2950 } 2951 2952 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, 2953 // but using getBoundingClientRect to get a sub-pixel-accurate 2954 // result. 2955 function compensateForHScroll(display) { 2956 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; 2957 } 2958 2959 // DISPLAY DRAWING 2960 2961 function DisplayUpdate(cm, viewport, force) { 2962 var display = cm.display; 2963 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); 2971 this.force = force; 2972 this.dims = getDimensions(cm); 2973 this.events = []; 2974 } 2975 2976 DisplayUpdate.prototype.signal = function(emitter, type) { 2977 if (hasHandler(emitter, type)) 2978 this.events.push(arguments); 2979 }; 2980 DisplayUpdate.prototype.finish = function() { 2981 for (var i = 0; i < this.events.length; i++) 2982 signal.apply(null, this.events[i]); 2983 }; 2984 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; 2993 } 2994 } 2995 2996 // Does the actual updating of the line display. Bails out 2997 // (returning false) when there is nothing to be done and forced is 2998 // false. 2999 function updateDisplayIfNeeded(cm, update) { 3000 var display = cm.display, doc = cm.doc; 3001 3002 if (update.editorIsHidden) { 3003 resetView(cm); 3004 return false; 3005 } 3006 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) 3012 return false; 3013 3014 if (maybeUpdateLineNumberWidth(cm)) { 3015 resetView(cm); 3016 update.dims = getDimensions(cm); 3017 } 3018 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); 3028 } 3029 3030 var different = from != display.viewFrom || to != display.viewTo || 3031 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; 3032 adjustView(cm, from, to); 3033 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"; 3037 3038 var toUpdate = countDirtyView(cm); 3039 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && 3040 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) 3041 return false; 3042 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(); 3053 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; 3059 3060 if (different) { 3061 display.lastWrapHeight = update.wrapperHeight; 3062 display.lastWrapWidth = update.wrapperWidth; 3063 startWorker(cm, 400); 3064 } 3065 3066 display.updateLineNumbers = null; 3067 3068 return true; 3069 } 3070 3071 function postUpdateDisplay(cm, update) { 3072 var viewport = update.viewport; 3073 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) 3083 break; 3084 } 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); 3091 } 3092 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; 3097 } 3098 } 3099 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); 3109 update.finish(); 3110 } 3111 } 3112 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"; 3117 } 3118 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; 3130 prevBottom = bot; 3131 } else { 3132 var box = cur.node.getBoundingClientRect(); 3133 height = box.bottom - box.top; 3134 } 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]); 3142 } 3143 } 3144 } 3145 3146 // Read and store the height of line widgets associated with the 3147 // given line. 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; 3151 } 3152 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; 3161 } 3162 return {fixedPos: compensateForHScroll(d), 3163 gutterTotalWidth: d.gutters.offsetWidth, 3164 gutterLeft: left, 3165 gutterWidth: width, 3166 wrapperWidth: d.wrapper.clientWidth}; 3167 } 3168 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 3172 // date. 3173 function patchDisplay(cm, updateNumbersFrom, dims) { 3174 var display = cm.display, lineNumbers = cm.options.lineNumbers; 3175 var container = display.lineDiv, cur = container.firstChild; 3176 3177 function rm(node) { 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"; 3182 else 3183 node.parentNode.removeChild(node); 3184 return next; 3185 } 3186 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); 3203 } 3204 if (updateNumber) { 3205 removeChildren(lineView.lineNumber); 3206 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); 3207 } 3208 cur = lineView.node.nextSibling; 3209 } 3210 lineN += lineView.size; 3211 } 3212 while (cur) cur = rm(cur); 3213 } 3214 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 3217 // DOM structure. 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); 3225 } 3226 lineView.changes = null; 3227 } 3228 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; 3238 } 3239 return lineView.node; 3240 } 3241 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; } 3248 } else if (cls) { 3249 var wrap = ensureLineWrapped(lineView); 3250 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); 3251 } 3252 } 3253 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; 3261 return ext.built; 3262 } 3263 return buildLineContent(cm, lineView); 3264 } 3265 3266 // Redraw the line's text. Interacts with the background and text 3267 // classes because the mode may output tokens that influence these 3268 // classes. 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); 3279 } else if (cls) { 3280 lineView.text.className = cls; 3281 } 3282 } 3283 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 || ""; 3292 } 3293 3294 function updateLineGutter(cm, lineView, lineN, dims) { 3295 if (lineView.gutter) { 3296 lineView.node.removeChild(lineView.gutter); 3297 lineView.gutter = null; 3298 } 3299 if (lineView.gutterBackground) { 3300 lineView.node.removeChild(lineView.gutterBackground); 3301 lineView.gutterBackground = null; 3302 } 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); 3309 } 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]; 3327 if (found) 3328 gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + 3329 dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")); 3330 } 3331 } 3332 } 3333 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); 3340 } 3341 insertLineWidgets(cm, lineView, dims); 3342 } 3343 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; 3350 3351 updateLineClasses(lineView); 3352 updateLineGutter(cm, lineView, lineN, dims); 3353 insertLineWidgets(cm, lineView, dims); 3354 return lineView.node; 3355 } 3356 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); 3363 } 3364 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); 3375 else 3376 wrap.appendChild(node); 3377 signalLater(widget, "redraw"); 3378 } 3379 } 3380 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"; 3389 } 3390 node.style.width = width + "px"; 3391 } 3392 if (widget.coverGutter) { 3393 node.style.zIndex = 5; 3394 node.style.position = "relative"; 3395 if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"; 3396 } 3397 } 3398 3399 // POSITION OBJECT 3400 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; 3405 }; 3406 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; }; 3410 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; } 3414 3415 // INPUT HANDLING 3416 3417 function ensureFocus(cm) { 3418 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } 3419 } 3420 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; 3425 3426 function applyTextInput(cm, inserted, deleted, sel, origin) { 3427 var doc = cm.doc; 3428 cm.display.shift = false; 3429 if (!sel) sel = doc.sel; 3430 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) { 3437 multiPaste = []; 3438 for (var i = 0; i < lastCopied.text.length; i++) 3439 multiPaste.push(doc.splitLines(lastCopied.text[i])); 3440 } 3441 } else if (textLines.length == sel.ranges.length) { 3442 multiPaste = map(textLines, function(l) { return [l]; }); 3443 } 3444 } 3445 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) 3457 } 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); 3463 } 3464 if (inserted && !paste) 3465 triggerElectric(cm, inserted); 3466 3467 ensureCursorVisible(cm); 3468 cm.curOp.updateInput = updateInput; 3469 cm.curOp.typing = true; 3470 cm.state.pasteIncoming = cm.state.cutIncoming = false; 3471 } 3472 3473 function handlePaste(e, cm) { 3474 var pasted = e.clipboardData && e.clipboardData.getData("text/plain"); 3475 if (pasted) { 3476 e.preventDefault(); 3477 if (!cm.isReadOnly() && !cm.options.disableInput) 3478 runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); }); 3479 return true; 3480 } 3481 } 3482 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; 3487 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"); 3497 break; 3498 } 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"); 3502 } 3503 if (indented) signalLater(cm, "electricInput", cm, range.head.line); 3504 } 3505 } 3506 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)); 3514 } 3515 return {text: text, ranges: ranges}; 3516 } 3517 3518 function disableBrowserMagic(field) { 3519 field.setAttribute("autocorrect", "off"); 3520 field.setAttribute("autocapitalize", "off"); 3521 field.setAttribute("spellcheck", "false"); 3522 } 3523 3524 // TEXTAREA INPUT STYLE 3525 3526 function TextareaInput(cm) { 3527 this.cm = cm; 3528 // See input.poll and input.reset 3529 this.prevInput = ""; 3530 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; 3543 }; 3544 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); 3557 return div; 3558 } 3559 3560 TextareaInput.prototype = copyObj({ 3561 init: function(display) { 3562 var input = this, cm = this.cm; 3563 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); 3570 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"; 3573 3574 on(te, "input", function() { 3575 if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null; 3576 input.poll(); 3577 }); 3578 3579 on(te, "paste", function(e) { 3580 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return 3581 3582 cm.state.pasteIncoming = true; 3583 input.fastPoll(); 3584 }); 3585 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"); 3594 selectInput(te); 3595 } 3596 } else if (!cm.options.lineWiseCopyCut) { 3597 return; 3598 } else { 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); 3603 } else { 3604 input.prevInput = ""; 3605 te.value = ranges.text.join("\n"); 3606 selectInput(te); 3607 } 3608 } 3609 if (e.type == "cut") cm.state.cutIncoming = true; 3610 } 3611 on(te, "cut", prepareCopyCut); 3612 on(te, "copy", prepareCopyCut); 3613 3614 on(display.scroller, "paste", function(e) { 3615 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return; 3616 cm.state.pasteIncoming = true; 3617 input.focus(); 3618 }); 3619 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); 3623 }); 3624 3625 on(te, "compositionstart", function() { 3626 var start = cm.getCursor("from"); 3627 if (input.composing) input.composing.range.clear() 3628 input.composing = { 3629 start: start, 3630 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) 3631 }; 3632 }); 3633 on(te, "compositionend", function() { 3634 if (input.composing) { 3635 input.poll(); 3636 input.composing.range.clear(); 3637 input.composing = null; 3638 } 3639 }); 3640 }, 3641 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); 3646 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)); 3655 } 3656 3657 return result; 3658 }, 3659 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"; 3667 } 3668 }, 3669 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; 3687 } 3688 this.inaccurateSelection = minimal; 3689 }, 3690 3691 getField: function() { return this.textarea; }, 3692 3693 supportsTouch: function() { return false; }, 3694 3695 focus: function() { 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 3699 } 3700 }, 3701 3702 blur: function() { this.textarea.blur(); }, 3703 3704 resetPosition: function() { 3705 this.wrapper.style.top = this.wrapper.style.left = 0; 3706 }, 3707 3708 receivedFocus: function() { this.slowPoll(); }, 3709 3710 // Poll for input changes, using the normal rate of polling. This 3711 // runs as long as the editor is focused. 3712 slowPoll: function() { 3713 var input = this; 3714 if (input.pollingFast) return; 3715 input.polling.set(this.cm.options.pollInterval, function() { 3716 input.poll(); 3717 if (input.cm.state.focused) input.slowPoll(); 3718 }); 3719 }, 3720 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; 3727 function p() { 3728 var changed = input.poll(); 3729 if (!changed && !missed) {missed = true; input.polling.set(60, p);} 3730 else {input.pollingFast = false; input.slowPoll();} 3731 } 3732 input.polling.set(20, p); 3733 }, 3734 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). 3741 poll: function() { 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) 3750 return false; 3751 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(); 3761 return false; 3762 } 3763 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"); } 3768 } 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; 3772 3773 var self = this; 3774 runInOp(cm, function() { 3775 applyTextInput(cm, text.slice(same), prevInput.length - same, 3776 null, self.composing ? "*compose" : null); 3777 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; 3781 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"}); 3786 } 3787 }); 3788 return true; 3789 }, 3790 3791 ensurePolled: function() { 3792 if (this.pollingFast && this.poll()) this.pollingFast = false; 3793 }, 3794 3795 onKeyPress: function() { 3796 if (ie && ie_version >= 9) this.hasSelection = null; 3797 this.fastPoll(); 3798 }, 3799 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. 3804 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); 3810 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); 3827 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 3830 // it got selected. 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 3836 te.value = extval; 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; 3842 } 3843 } 3844 function rehide() { 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); 3849 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(); 3859 }; 3860 display.detectingSelectAll = setTimeout(poll, 200); 3861 } 3862 } 3863 3864 if (ie && ie_version >= 9) prepareSelectAllHack(); 3865 if (captureRightClick) { 3866 e_stop(e); 3867 var mouseup = function() { 3868 off(window, "mouseup", mouseup); 3869 setTimeout(rehide, 20); 3870 }; 3871 on(window, "mouseup", mouseup); 3872 } else { 3873 setTimeout(rehide, 50); 3874 } 3875 }, 3876 3877 readOnlyChanged: function(val) { 3878 if (!val) this.reset(); 3879 }, 3880 3881 setUneditable: nothing, 3882 3883 needsContentAttribute: false 3884 }, TextareaInput.prototype); 3885 3886 // CONTENTEDITABLE INPUT STYLE 3887 3888 function ContentEditableInput(cm) { 3889 this.cm = cm; 3890 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; 3891 this.polling = new Delayed(); 3892 this.gracePeriod = false; 3893 } 3894 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); 3900 3901 on(div, "paste", function(e) { 3902 if (!signalDOMEvent(cm, e)) handlePaste(e, cm); 3903 }) 3904 3905 on(div, "compositionstart", function(e) { 3906 var data = e.data; 3907 input.composing = {sel: cm.doc.sel, data: data, startData: data}; 3908 if (!data) return; 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)); 3915 }); 3916 on(div, "compositionupdate", function(e) { 3917 input.composing.data = e.data; 3918 }); 3919 on(div, "compositionend", function(e) { 3920 var ours = input.composing; 3921 if (!ours) return; 3922 if (e.data != ours.startData && !/\u200b/.test(e.data)) 3923 ours.data = e.data; 3924 // Need a small delay to prevent other code (input event, 3925 // selection polling) from doing damage when fired right after 3926 // compositionend. 3927 setTimeout(function() { 3928 if (!ours.handled) 3929 input.applyComposition(ours); 3930 if (input.composing == ours) 3931 input.composing = null; 3932 }, 50); 3933 }); 3934 3935 on(div, "touchstart", function() { 3936 input.forceCompositionEnd(); 3937 }); 3938 3939 on(div, "input", function() { 3940 if (input.composing) return; 3941 if (cm.isReadOnly() || !input.pollContent()) 3942 runInOp(input.cm, function() {regChange(cm);}); 3943 }); 3944 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) { 3951 return; 3952 } else { 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"); 3959 }); 3960 } 3961 } 3962 // iOS exposes the clipboard API, but seems to discard content inserted into it 3963 if (e.clipboardData && !ios) { 3964 e.preventDefault(); 3965 e.clipboardData.clearData(); 3966 e.clipboardData.setData("text/plain", lastCopied.text.join("\n")); 3967 } else { 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; 3973 selectInput(te); 3974 setTimeout(function() { 3975 cm.display.lineSpace.removeChild(kludge); 3976 hadFocus.focus(); 3977 }, 50); 3978 } 3979 } 3980 on(div, "copy", onCopyCut); 3981 on(div, "cut", onCopyCut); 3982 }, 3983 3984 prepareSelection: function() { 3985 var result = prepareSelection(this.cm, false); 3986 result.focus = this.cm.state.focused; 3987 return result; 3988 }, 3989 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); 3994 }, 3995 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) 4003 return; 4004 4005 var start = posToDOM(this.cm, prim.from()); 4006 var end = posToDOM(this.cm, prim.to()); 4007 if (!start && !end) return; 4008 4009 var view = this.cm.display.view; 4010 var old = sel.rangeCount && sel.getRangeAt(0); 4011 if (!start) { 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]}; 4017 } 4018 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 4021 if (rng) { 4022 if (!gecko && this.cm.state.focused) { 4023 sel.collapse(start.node, start.offset); 4024 if (!rng.collapsed) sel.addRange(rng); 4025 } else { 4026 sel.removeAllRanges(); 4027 sel.addRange(rng); 4028 } 4029 if (old && sel.anchorNode == null) sel.addRange(old); 4030 else if (gecko) this.startGracePeriod(); 4031 } 4032 this.rememberSelection(); 4033 }, 4034 4035 startGracePeriod: function() { 4036 var input = this; 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; }); 4042 }, 20); 4043 }, 4044 4045 showMultipleSelections: function(info) { 4046 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); 4047 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); 4048 }, 4049 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; 4054 }, 4055 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); 4061 }, 4062 4063 focus: function() { 4064 if (this.cm.options.readOnly != "nocursor") this.div.focus(); 4065 }, 4066 blur: function() { this.div.blur(); }, 4067 getField: function() { return this.div; }, 4068 4069 supportsTouch: function() { return true; }, 4070 4071 receivedFocus: function() { 4072 var input = this; 4073 if (this.selectionInEditor()) 4074 this.pollSelection(); 4075 else 4076 runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; }); 4077 4078 function poll() { 4079 if (input.cm.state.focused) { 4080 input.pollSelection(); 4081 input.polling.set(input.cm.options.pollInterval, poll); 4082 } 4083 } 4084 this.polling.set(this.cm.options.pollInterval, poll); 4085 }, 4086 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; 4091 }, 4092 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; 4102 }); 4103 } 4104 }, 4105 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; 4110 4111 var fromIndex; 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; 4115 } else { 4116 var fromLine = lineNo(display.view[fromIndex].line); 4117 var fromNode = display.view[fromIndex - 1].node.nextSibling; 4118 } 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; 4123 } else { 4124 var toLine = lineNo(display.view[toIndex + 1].line) - 1; 4125 var toNode = display.view[toIndex + 1].node.previousSibling; 4126 } 4127 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++; } 4133 else break; 4134 } 4135 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)) 4139 ++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)) 4145 ++cutEnd; 4146 4147 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd); 4148 newText[0] = newText[0].slice(cutFront); 4149 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"); 4154 return true; 4155 } 4156 }, 4157 4158 ensurePolled: function() { 4159 this.forceCompositionEnd(); 4160 }, 4161 reset: function() { 4162 this.forceCompositionEnd(); 4163 }, 4164 forceCompositionEnd: function() { 4165 if (!this.composing || this.composing.handled) return; 4166 this.applyComposition(this.composing); 4167 this.composing.handled = true; 4168 this.div.blur(); 4169 this.div.focus(); 4170 }, 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); 4176 }, 4177 4178 setUneditable: function(node) { 4179 node.contentEditable = "false" 4180 }, 4181 4182 onKeyPress: function(e) { 4183 e.preventDefault(); 4184 if (!this.cm.isReadOnly()) 4185 operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); 4186 }, 4187 4188 readOnlyChanged: function(val) { 4189 this.div.contentEditable = String(val != "nocursor") 4190 }, 4191 4192 onContextMenu: nothing, 4193 resetPosition: nothing, 4194 4195 needsContentAttribute: true 4196 }, ContentEditableInput.prototype); 4197 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); 4203 4204 var order = getOrder(line), side = "left"; 4205 if (order) { 4206 var partPos = getBidiPartAt(order, pos.ch); 4207 side = partPos % 2 ? "right" : "left"; 4208 } 4209 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); 4210 result.offset = result.collapse == "right" ? result.end : result.start; 4211 return result; 4212 } 4213 4214 function badPos(pos, bad) { if (bad) pos.bad = true; return pos; } 4215 4216 function domToPos(cm, node, offset) { 4217 var lineNode; 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; 4222 } else { 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; 4226 } 4227 } 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); 4232 } 4233 } 4234 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) { 4239 bad = true; 4240 node = wrapper.childNodes[offset]; 4241 offset = 0; 4242 if (!node) { 4243 var line = lineView.rest ? lst(lineView.rest) : lineView.line; 4244 return badPos(Pos(lineNo(line), line.text.length), bad); 4245 } 4246 } 4247 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; 4252 } 4253 while (topNode.parentNode != wrapper) topNode = topNode.parentNode; 4254 var measure = lineView.measure, maps = measure.maps; 4255 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); 4266 } 4267 } 4268 } 4269 } 4270 var found = find(textNode, topNode, offset); 4271 if (found) return badPos(found, bad); 4272 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); 4276 if (found) 4277 return badPos(Pos(found.line, found.ch - dist), bad); 4278 else 4279 dist += after.textContent.length; 4280 } 4281 for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { 4282 found = find(before, before.firstChild, -1); 4283 if (found) 4284 return badPos(Pos(found.line, found.ch + dist), bad); 4285 else 4286 dist += after.textContent.length; 4287 } 4288 } 4289 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, ""); 4298 text += cmText; 4299 return; 4300 } 4301 var markerID = node.getAttribute("cm-marker"), range; 4302 if (markerID) { 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); 4306 return; 4307 } 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)) 4312 closing = true; 4313 } else if (node.nodeType == 3) { 4314 var val = node.nodeValue; 4315 if (!val) return; 4316 if (closing) { 4317 text += lineSep; 4318 closing = false; 4319 } 4320 text += val; 4321 } 4322 } 4323 for (;;) { 4324 walk(from); 4325 if (from == to) break; 4326 from = from.nextSibling; 4327 } 4328 return text; 4329 } 4330 4331 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; 4332 4333 // SELECTION / CURSOR 4334 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; 4343 } 4344 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; 4353 } 4354 return true; 4355 }, 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); 4360 }, 4361 somethingSelected: function() { 4362 for (var i = 0; i < this.ranges.length; i++) 4363 if (!this.ranges[i].empty()) return true; 4364 return false; 4365 }, 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) 4371 return i; 4372 } 4373 return -1; 4374 } 4375 }; 4376 4377 function Range(anchor, head) { 4378 this.anchor = anchor; this.head = head; 4379 } 4380 4381 Range.prototype = { 4382 from: function() { return minPos(this.anchor, this.head); }, 4383 to: function() { return maxPos(this.anchor, this.head); }, 4384 empty: function() { 4385 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch; 4386 } 4387 }; 4388 4389 // Take an unsorted, potentially overlapping set of ranges, and 4390 // build a selection out of it. 'Consumes' ranges array (modifying 4391 // it). 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)); 4403 } 4404 } 4405 return new Selection(ranges, primIndex); 4406 } 4407 4408 function simpleSelection(anchor, head) { 4409 return new Selection([new Range(anchor, head || anchor)], 0); 4410 } 4411 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); 4420 } 4421 function clipToLen(pos, linelen) { 4422 var ch = pos.ch; 4423 if (ch == null || ch > linelen) return Pos(pos.line, linelen); 4424 else if (ch < 0) return Pos(pos.line, 0); 4425 else return pos; 4426 } 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]); 4430 return out; 4431 } 4432 4433 // SELECTION UPDATES 4434 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. 4438 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; 4446 if (other) { 4447 var posBefore = cmp(head, anchor) < 0; 4448 if (posBefore != (cmp(other, anchor) < 0)) { 4449 anchor = head; 4450 head = other; 4451 } else if (posBefore != (cmp(head, other) < 0)) { 4452 head = other; 4453 } 4454 } 4455 return new Range(anchor, head); 4456 } else { 4457 return new Range(other || head, head); 4458 } 4459 } 4460 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); 4464 } 4465 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); 4473 } 4474 4475 // Updates a single range in the selection. 4476 function replaceOneSelection(doc, i, range, options) { 4477 var ranges = doc.sel.ranges.slice(0); 4478 ranges[i] = range; 4479 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); 4480 } 4481 4482 // Reset the selection to a single range. 4483 function setSimpleSelection(doc, anchor, head, options) { 4484 setSelection(doc, simpleSelection(anchor, head), options); 4485 } 4486 4487 // Give beforeSelectionChange handlers a change to influence a 4488 // selection update. 4489 function filterSelectionChange(doc, sel, options) { 4490 var obj = { 4491 ranges: sel.ranges, 4492 update: function(ranges) { 4493 this.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)); 4497 }, 4498 origin: options && options.origin 4499 }; 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); 4503 else return sel; 4504 } 4505 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); 4511 } else { 4512 setSelection(doc, sel, options); 4513 } 4514 } 4515 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); 4520 } 4521 4522 function setSelectionNoUndo(doc, sel, options) { 4523 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) 4524 sel = filterSelectionChange(doc, sel, options); 4525 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)); 4529 4530 if (!(options && options.scroll === false) && doc.cm) 4531 ensureCursorVisible(doc.cm); 4532 } 4533 4534 function setSelectionInner(doc, sel) { 4535 if (sel.equals(doc.sel)) return; 4536 4537 doc.sel = sel; 4538 4539 if (doc.cm) { 4540 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; 4541 signalCursorActivity(doc.cm); 4542 } 4543 signalLater(doc, "cursorActivity", doc); 4544 } 4545 4546 // Verify that the selection does not partially select any atomic 4547 // marked ranges. 4548 function reCheckSelection(doc) { 4549 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll); 4550 } 4551 4552 // Return a selection that does not partially select any atomic 4553 // ranges. 4554 function skipAtomicInSelection(doc, sel, bias, mayClear) { 4555 var out; 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); 4564 } 4565 } 4566 return out ? normalizeSelection(out, sel.primIndex) : sel; 4567 } 4568 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))) { 4575 if (mayClear) { 4576 signal(m, "beforeCursorEnter"); 4577 if (m.explicitlyCleared) { 4578 if (!line.markedSpans) break; 4579 else {--i; continue;} 4580 } 4581 } 4582 if (!m.atomic) continue; 4583 4584 if (oldPos) { 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); 4590 } 4591 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; 4596 } 4597 } 4598 return pos; 4599 } 4600 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)); 4608 if (!found) { 4609 doc.cantEdit = true; 4610 return Pos(doc.first, 0); 4611 } 4612 return found; 4613 } 4614 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)); 4618 else return null; 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); 4621 else return null; 4622 } else { 4623 return new Pos(pos.line, pos.ch + dir); 4624 } 4625 } 4626 4627 // SELECTION DRAWING 4628 4629 function updateSelection(cm) { 4630 cm.display.input.showSelection(cm.display.input.prepareSelection()); 4631 } 4632 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(); 4637 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); 4645 if (!collapsed) 4646 drawSelectionRange(cm, range, selFragment); 4647 } 4648 return result; 4649 } 4650 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); 4654 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"; 4659 4660 if (pos.other) { 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"; 4667 } 4668 } 4669 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; 4676 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")); 4684 } 4685 4686 function drawForLine(line, fromArg, toArg) { 4687 var lineObj = getLine(doc, line); 4688 var lineLen = lineObj.text.length; 4689 var start, end; 4690 function coords(ch, bias) { 4691 return charCoords(cm, Pos(line, ch), "div", lineObj, bias); 4692 } 4693 4694 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { 4695 var leftPos = coords(from, "left"), rightPos, left, right; 4696 if (from == to) { 4697 rightPos = leftPos; 4698 left = right = leftPos.left; 4699 } else { 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; 4704 } 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); 4708 left = leftSide; 4709 if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); 4710 } 4711 if (toArg == null && to == lineLen) right = rightSide; 4712 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) 4713 start = leftPos; 4714 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) 4715 end = rightPos; 4716 if (left < leftSide + 1) left = leftSide; 4717 add(left, rightPos.top, right - left, rightPos.bottom); 4718 }); 4719 return {start: start, end: end}; 4720 } 4721 4722 var sFrom = range.from(), sTo = range.to(); 4723 if (sFrom.line == sTo.line) { 4724 drawForLine(sFrom.line, sFrom.ch, sTo.ch); 4725 } else { 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; 4730 if (singleVLine) { 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); 4734 } else { 4735 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); 4736 } 4737 } 4738 if (leftEnd.bottom < rightStart.top) 4739 add(leftSide, leftEnd.bottom, null, rightStart.top); 4740 } 4741 4742 output.appendChild(fragment); 4743 } 4744 4745 // Cursor-blinking 4746 function restartBlink(cm) { 4747 if (!cm.state.focused) return; 4748 var display = cm.display; 4749 clearInterval(display.blinker); 4750 var on = true; 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"; 4758 } 4759 4760 // HIGHLIGHT WORKER 4761 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)); 4765 } 4766 4767 function highlightWorker(cm) { 4768 var doc = cm.doc; 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 = []; 4774 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); 4788 } else { 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; 4792 } 4793 ++doc.frontier; 4794 if (+new Date > end) { 4795 startWorker(cm, cm.options.workDelay); 4796 return true; 4797 } 4798 }); 4799 if (changedLines.length) runInOp(cm, function() { 4800 for (var i = 0; i < changedLines.length; i++) 4801 regLineChange(cm, changedLines[i], "text"); 4802 }); 4803 } 4804 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 4809 // parse correctly. 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; 4821 } 4822 } 4823 return minline; 4824 } 4825 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; 4836 ++pos; 4837 }); 4838 if (precise) doc.frontier = pos; 4839 return state; 4840 } 4841 4842 // POSITION MEASUREMENT 4843 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; 4852 return data; 4853 } 4854 4855 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; } 4856 function displayWidth(cm) { 4857 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth; 4858 } 4859 function displayHeight(cm) { 4860 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight; 4861 } 4862 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 4866 // height. 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 = []; 4872 if (wrapping) { 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); 4879 } 4880 } 4881 heights.push(rect.bottom - rect.top); 4882 } 4883 } 4884 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}; 4897 } 4898 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); 4905 view.lineN = lineN; 4906 var built = view.built = buildLineContent(cm, view); 4907 view.text = built.pre; 4908 removeChildrenAndAdd(cm.display.lineMeasure, built.pre); 4909 return view; 4910 } 4911 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); 4916 } 4917 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) 4924 return ext; 4925 } 4926 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 4931 // only done once. 4932 function prepareMeasureForLine(cm, line) { 4933 var lineN = lineNo(line); 4934 var view = findViewForLine(cm, lineN); 4935 if (view && !view.text) { 4936 view = null; 4937 } else if (view && view.changes) { 4938 updateLineForChanges(cm, view, lineN, getDimensions(cm)); 4939 cm.curOp.forceUpdate = true; 4940 } 4941 if (!view) 4942 view = updateExternalMeasurement(cm, line); 4943 4944 var info = mapFromLineView(view, line, lineN); 4945 return { 4946 line: line, view: view, rect: null, 4947 map: info.map, cache: info.cache, before: info.before, 4948 hasHeights: false 4949 }; 4950 } 4951 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]; 4959 } else { 4960 if (!prepared.rect) 4961 prepared.rect = prepared.view.text.getBoundingClientRect(); 4962 if (!prepared.hasHeights) { 4963 ensureLineHeights(cm, prepared.view, prepared.rect); 4964 prepared.hasHeights = true; 4965 } 4966 found = measureCharInner(cm, prepared, ch, bias); 4967 if (!found.bogus) prepared.cache[key] = found; 4968 } 4969 return {left: found.left, right: found.right, 4970 top: varHeight ? found.rtop : found.top, 4971 bottom: varHeight ? found.rbottom : found.bottom}; 4972 } 4973 4974 var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; 4975 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]; 4982 if (ch < mStart) { 4983 start = 0; end = 1; 4984 collapse = "left"; 4985 } else if (ch < mEnd) { 4986 start = ch - mStart; 4987 end = start + 1; 4988 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { 4989 end = mEnd - mStart; 4990 start = end - 1; 4991 if (ch >= mEnd) collapse = "right"; 4992 } 4993 if (start != null) { 4994 node = map[i + 2]; 4995 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) 4996 collapse = bias; 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]; 5000 collapse = "left"; 5001 } 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]; 5005 collapse = "right"; 5006 } 5007 break; 5008 } 5009 } 5010 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}; 5011 } 5012 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; 5016 5017 var rect; 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(); 5026 if (rects.length) 5027 rect = rects[bias == "right" ? rects.length - 1 : 0]; 5028 else 5029 rect = nullRect; 5030 } else { 5031 rect = range(node, start, end).getBoundingClientRect() || nullRect; 5032 } 5033 if (rect.left || rect.right || start == 0) break; 5034 end = start; 5035 start = start - 1; 5036 collapse = "right"; 5037 } 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"; 5041 var rects; 5042 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) 5043 rect = rects[bias == "right" ? rects.length - 1 : 0]; 5044 else 5045 rect = node.getBoundingClientRect(); 5046 } 5047 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { 5048 var rSpan = node.parentNode.getClientRects()[0]; 5049 if (rSpan) 5050 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; 5051 else 5052 rect = nullRect; 5053 } 5054 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; } 5066 5067 return result; 5068 } 5069 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)) 5075 return rect; 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}; 5080 } 5081 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] = {}; 5088 } 5089 } 5090 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]); 5096 } 5097 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; 5103 } 5104 5105 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; } 5106 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; } 5107 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", 5111 // or "page". 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; 5116 } 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; 5127 } 5128 rect.top += yOff; rect.bottom += yOff; 5129 return rect; 5130 } 5131 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; 5145 } 5146 5147 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); 5148 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; 5149 } 5150 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); 5154 } 5155 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); 5166 } 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); 5172 right = true; 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; 5176 right = false; 5177 } 5178 if (right && ch == part.to && ch > part.from) return get(ch - 1); 5179 return get(ch, right); 5180 } 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); 5186 return val; 5187 } 5188 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}; 5197 } 5198 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 5204 // vertical range. 5205 function PosWithInfo(line, ch, outside, xRel) { 5206 var pos = Pos(line, ch); 5207 pos.xRel = xRel; 5208 if (outside) pos.outside = true; 5209 return pos; 5210 } 5211 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) { 5215 var doc = cm.doc; 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; 5219 if (lineN > last) 5220 return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1); 5221 if (x < 0) x = 0; 5222 5223 var lineObj = getLine(doc, lineN); 5224 for (;;) { 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); 5230 else 5231 return found; 5232 } 5233 } 5234 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); 5239 5240 function getX(ch) { 5241 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure); 5242 wrongLine = true; 5243 if (innerOff > sp.bottom) return sp.left - adjust; 5244 else if (innerOff < sp.top) return sp.left + adjust; 5245 else wrongLine = false; 5246 return sp.left; 5247 } 5248 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; 5252 5253 if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1); 5254 // Do a binary search between these bounds. 5255 for (;;) { 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); 5262 return pos; 5263 } 5264 var step = Math.ceil(dist / 2), middle = from + step; 5265 if (bidi) { 5266 middle = from; 5267 for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); 5268 } 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;} 5272 } 5273 } 5274 5275 var measureText; 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")); 5286 } 5287 measureText.appendChild(document.createTextNode("x")); 5288 } 5289 removeChildrenAndAdd(display.measure, measureText); 5290 var height = measureText.offsetHeight / 50; 5291 if (height > 3) display.cachedTextHeight = height; 5292 removeChildren(display.measure); 5293 return height || 1; 5294 } 5295 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; 5304 return width || 10; 5305 } 5306 5307 // OPERATIONS 5308 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. 5314 5315 var operationGroup = null; 5316 5317 var nextOpId = 0; 5318 // Start a new operation. 5319 function startOperation(cm) { 5320 cm.curOp = { 5321 cm: 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 5334 focus: false, 5335 id: ++nextOpId // Unique ID 5336 }; 5337 if (operationGroup) { 5338 operationGroup.ops.push(cm.curOp); 5339 } else { 5340 cm.curOp.ownsGroup = operationGroup = { 5341 ops: [cm.curOp], 5342 delayedCallbacks: [] 5343 }; 5344 } 5345 } 5346 5347 function fireCallbacksForOps(group) { 5348 // Calls delayed callbacks and cursorActivity handlers until no 5349 // new ones appear 5350 var callbacks = group.delayedCallbacks, i = 0; 5351 do { 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); 5359 } 5360 } while (i < callbacks.length); 5361 } 5362 5363 // Finish an operation, updating the display and signalling delayed events 5364 function endOperation(cm) { 5365 var op = cm.curOp, group = op.ownsGroup; 5366 if (!group) return; 5367 5368 try { fireCallbacksForOps(group); } 5369 finally { 5370 operationGroup = null; 5371 for (var i = 0; i < group.ops.length; i++) 5372 group.ops[i].cm.curOp = null; 5373 endOperations(group); 5374 } 5375 } 5376 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]); 5391 } 5392 5393 function endOperation_R1(op) { 5394 var cm = op.cm, display = cm.display; 5395 maybeClipScrollbars(cm); 5396 if (op.updateMaxLine) findMaxLine(cm); 5397 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); 5404 } 5405 5406 function endOperation_W1(op) { 5407 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); 5408 } 5409 5410 function endOperation_R2(op) { 5411 var cm = op.cm, display = cm.display; 5412 if (op.updatedDisplay) updateHeightsInViewport(cm); 5413 5414 op.barMeasure = measureForScrollbars(cm); 5415 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)); 5425 } 5426 5427 if (op.updatedDisplay || op.selectionChanged) 5428 op.preparedSelection = display.input.prepareSelection(op.focus); 5429 } 5430 5431 function endOperation_W2(op) { 5432 var cm = op.cm; 5433 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; 5439 } 5440 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); 5448 5449 if (op.selectionChanged) restartBlink(cm); 5450 5451 if (cm.state.focused && op.updateInput) 5452 cm.display.input.reset(op.typing); 5453 if (takeFocus) ensureFocus(op.cm); 5454 } 5455 5456 function endOperation_finish(op) { 5457 var cm = op.cm, display = cm.display, doc = cm.doc; 5458 5459 if (op.updatedDisplay) postUpdateDisplay(cm, op.update); 5460 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; 5464 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; 5470 } 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); 5476 } 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); 5482 } 5483 5484 // Fire events for markers that are hidden/unidden by editing or 5485 // undoing 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"); 5491 5492 if (display.wrapper.offsetHeight) 5493 doc.scrollTop = cm.display.scroller.scrollTop; 5494 5495 // Fire change events, and delayed event handlers 5496 if (op.changeObjs) 5497 signal(cm, "changes", cm, op.changeObjs); 5498 if (op.update) 5499 op.update.finish(); 5500 } 5501 5502 // Run the given function in an operation 5503 function runInOp(cm, f) { 5504 if (cm.curOp) return f(); 5505 startOperation(cm); 5506 try { return f(); } 5507 finally { endOperation(cm); } 5508 } 5509 // Wraps a function in an operation. Returns the wrapped function. 5510 function operation(cm, f) { 5511 return function() { 5512 if (cm.curOp) return f.apply(cm, arguments); 5513 startOperation(cm); 5514 try { return f.apply(cm, arguments); } 5515 finally { endOperation(cm); } 5516 }; 5517 } 5518 // Used to add methods to editor and doc instances, wrapping them in 5519 // operations. 5520 function methodOp(f) { 5521 return function() { 5522 if (this.curOp) return f.apply(this, arguments); 5523 startOperation(this); 5524 try { return f.apply(this, arguments); } 5525 finally { endOperation(this); } 5526 }; 5527 } 5528 function docMethodOp(f) { 5529 return function() { 5530 var cm = this.cm; 5531 if (!cm || cm.curOp) return f.apply(this, arguments); 5532 startOperation(cm); 5533 try { return f.apply(this, arguments); } 5534 finally { endOperation(cm); } 5535 }; 5536 } 5537 5538 // VIEW TRACKING 5539 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 5545 this.line = 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); 5552 } 5553 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; 5560 array.push(view); 5561 } 5562 return array; 5563 } 5564 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; 5575 5576 var display = cm.display; 5577 if (lendiff && to < display.viewTo && 5578 (display.updateLineNumbers == null || display.updateLineNumbers > from)) 5579 display.updateLineNumbers = from; 5580 5581 cm.curOp.viewChanged = true; 5582 5583 if (from >= display.viewTo) { // Change after 5584 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) 5585 resetView(cm); 5586 } else if (to <= display.viewFrom) { // Change before 5587 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { 5588 resetView(cm); 5589 } else { 5590 display.viewFrom += lendiff; 5591 display.viewTo += lendiff; 5592 } 5593 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap 5594 resetView(cm); 5595 } else if (from <= display.viewFrom) { // Top overlap 5596 var cut = viewCuttingPoint(cm, to, to + lendiff, 1); 5597 if (cut) { 5598 display.view = display.view.slice(cut.index); 5599 display.viewFrom = cut.lineN; 5600 display.viewTo += lendiff; 5601 } else { 5602 resetView(cm); 5603 } 5604 } else if (to >= display.viewTo) { // Bottom overlap 5605 var cut = viewCuttingPoint(cm, from, from, -1); 5606 if (cut) { 5607 display.view = display.view.slice(0, cut.index); 5608 display.viewTo = cut.lineN; 5609 } else { 5610 resetView(cm); 5611 } 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; 5620 } else { 5621 resetView(cm); 5622 } 5623 } 5624 5625 var ext = display.externalMeasured; 5626 if (ext) { 5627 if (to < ext.lineN) 5628 ext.lineN += lendiff; 5629 else if (from < ext.lineN + ext.size) 5630 display.externalMeasured = null; 5631 } 5632 } 5633 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; 5641 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); 5647 } 5648 5649 // Clear the view. 5650 function resetView(cm) { 5651 cm.display.viewFrom = cm.display.viewTo = cm.doc.first; 5652 cm.display.view = []; 5653 cm.display.viewOffset = 0; 5654 } 5655 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++) { 5664 n -= view[i].size; 5665 if (n < 0) return i; 5666 } 5667 } 5668 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++) 5674 n += view[i].size; 5675 if (n != oldN) { 5676 if (dir > 0) { 5677 if (index == view.length - 1) return null; 5678 diff = (n + view[index].size) - oldN; 5679 index++; 5680 } else { 5681 diff = n - oldN; 5682 } 5683 oldN += diff; newN += diff; 5684 } 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; 5688 index += dir; 5689 } 5690 return {index: index, lineN: newN}; 5691 } 5692 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; 5700 } else { 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)); 5710 } 5711 display.viewTo = to; 5712 } 5713 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; 5721 } 5722 return dirty; 5723 } 5724 5725 // EVENT HANDLERS 5726 5727 // Attach the necessary event handlers when initializing the editor 5728 function registerEventHandlers(cm) { 5729 var d = cm.display; 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); 5740 })); 5741 else 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);}); 5747 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; 5755 } 5756 }; 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; 5761 } 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; 5766 } 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; 5776 } 5777 } 5778 }); 5779 on(d.scroller, "touchmove", function() { 5780 if (d.activeTouch) d.activeTouch.moved = true; 5781 }); 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); 5791 else // Triple tap 5792 range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); 5793 cm.setSelection(range.anchor, range.head); 5794 cm.focus(); 5795 e_preventDefault(e); 5796 } 5797 finishTouch(); 5798 }); 5799 on(d.scroller, "touchcancel", finishTouch); 5800 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); 5808 } 5809 }); 5810 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);}); 5814 5815 // Prevent wrapper from ever scrolling 5816 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); 5817 5818 d.dragFunctions = { 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); }} 5824 }; 5825 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)); 5832 } 5833 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); 5844 } 5845 } 5846 5847 // Called when the window resizes 5848 function onResize(cm) { 5849 var d = cm.display; 5850 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) 5851 return; 5852 // Might be a text scaling operation, clear size caches. 5853 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; 5854 d.scrollbarsClipped = false; 5855 cm.setSize(); 5856 } 5857 5858 // MOUSE EVENTS 5859 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)) 5865 return true; 5866 } 5867 } 5868 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; 5877 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)); 5886 } 5887 return coords; 5888 } 5889 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; 5899 5900 if (eventInWidget(display, e)) { 5901 if (!webkit) { 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); 5906 } 5907 return; 5908 } 5909 if (clickInGutter(cm, e)) return; 5910 var start = posFromMouse(cm, e); 5911 window.focus(); 5912 5913 switch (e_button(e)) { 5914 case 1: 5915 // #3261: make sure, that we're not starting a second selection 5916 if (cm.state.selectingText) 5917 cm.state.selectingText(e); 5918 else if (start) 5919 leftButtonDown(cm, e, start); 5920 else if (e_target(e) == display.scroller) 5921 e_preventDefault(e); 5922 break; 5923 case 2: 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); 5928 break; 5929 case 3: 5930 if (captureRightClick) onContextMenu(cm, e); 5931 else delayBlurEvent(cm); 5932 break; 5933 } 5934 } 5935 5936 var lastClick, lastDoubleClick; 5937 function leftButtonDown(cm, e, start) { 5938 if (ie) setTimeout(bind(ensureFocus, cm), 0); 5939 else cm.curOp.focus = activeElt(); 5940 5941 var now = +new Date, type; 5942 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { 5943 type = "triple"; 5944 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { 5945 type = "double"; 5946 lastDoubleClick = {time: now, pos: start}; 5947 } else { 5948 type = "single"; 5949 lastClick = {time: now, pos: start}; 5950 } 5951 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); 5958 else 5959 leftButtonSelect(cm, e, start, type, modifier); 5960 } 5961 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); 5978 else 5979 display.input.focus(); 5980 } 5981 }); 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); 5989 } 5990 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); 5995 5996 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; 5997 if (addNew && !e.shiftKey) { 5998 ourIndex = doc.sel.contains(start); 5999 if (ourIndex > -1) 6000 ourRange = ranges[ourIndex]; 6001 else 6002 ourRange = new Range(start, start); 6003 } else { 6004 ourRange = doc.sel.primary(); 6005 ourIndex = doc.sel.primIndex; 6006 } 6007 6008 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) { 6009 type = "rect"; 6010 if (!addNew) ourRange = new Range(start, start); 6011 start = posFromMouse(cm, e, true, true); 6012 ourIndex = -1; 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); 6017 else 6018 ourRange = word; 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); 6023 else 6024 ourRange = line; 6025 } else { 6026 ourRange = extendRange(doc, ourRange, start); 6027 } 6028 6029 if (!addNew) { 6030 ourIndex = 0; 6031 setSelection(doc, new Selection([ourRange], 0), sel_mouse); 6032 startSel = doc.sel; 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"}); 6040 startSel = doc.sel; 6041 } else { 6042 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); 6043 } 6044 6045 var lastPos = start; 6046 function extendTo(pos) { 6047 if (cmp(lastPos, pos) == 0) return; 6048 lastPos = pos; 6049 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); 6058 if (left == right) 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)))); 6062 } 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); 6067 } else { 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); 6073 else 6074 var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))); 6075 if (cmp(range.anchor, anchor) > 0) { 6076 head = range.head; 6077 anchor = minPos(oldRange.from(), range.anchor); 6078 } else { 6079 head = range.anchor; 6080 anchor = maxPos(oldRange.to(), range.head); 6081 } 6082 } 6083 var ranges = startSel.ranges.slice(0); 6084 ranges[ourIndex] = new Range(clipPos(doc, anchor), head); 6085 setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse); 6086 } 6087 } 6088 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). 6094 var counter = 0; 6095 6096 function extend(e) { 6097 var curCount = ++counter; 6098 var cur = posFromMouse(cm, e, true, type == "rect"); 6099 if (!cur) return; 6100 if (cmp(cur, lastPos) != 0) { 6101 cm.curOp.focus = activeElt(); 6102 extendTo(cur); 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); 6106 } else { 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; 6111 extend(e); 6112 }), 50); 6113 } 6114 } 6115 6116 function done(e) { 6117 cm.state.selectingText = false; 6118 counter = Infinity; 6119 e_preventDefault(e); 6120 display.input.focus(); 6121 off(document, "mousemove", move); 6122 off(document, "mouseup", up); 6123 doc.history.lastSelOrigin = null; 6124 } 6125 6126 var move = operation(cm, function(e) { 6127 if (!e_button(e)) done(e); 6128 else extend(e); 6129 }); 6130 var up = operation(cm, done); 6131 cm.state.selectingText = up; 6132 on(document, "mousemove", move); 6133 on(document, "mouseup", up); 6134 } 6135 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); 6143 6144 var display = cm.display; 6145 var lineBox = display.lineDiv.getBoundingClientRect(); 6146 6147 if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e); 6148 mY -= lineBox.top - display.viewOffset; 6149 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); 6157 } 6158 } 6159 } 6160 6161 function clickInGutter(cm, e) { 6162 return gutterEvent(cm, e, "gutterClick", true); 6163 } 6164 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) 6167 var lastDrop = 0; 6168 6169 function onDrop(e) { 6170 var cm = this; 6171 clearDragCursor(cm); 6172 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) 6173 return; 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 6179 // and insert it. 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) 6185 return; 6186 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 = ""; 6191 text[i] = content; 6192 if (++read == n) { 6193 pos = clipPos(cm.doc, pos); 6194 var change = {from: pos, to: pos, 6195 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), 6196 origin: "paste"}; 6197 makeChange(cm.doc, change); 6198 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); 6199 } 6200 }); 6201 reader.readAsText(file); 6202 }; 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); 6210 return; 6211 } 6212 try { 6213 var text = e.dataTransfer.getData("Text"); 6214 if (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(); 6222 } 6223 } 6224 catch(e){} 6225 } 6226 } 6227 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; 6231 6232 e.dataTransfer.setData("Text", cm.getSelection()); 6233 e.dataTransfer.effectAllowed = "copyMove" 6234 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=="; 6240 if (presto) { 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; 6245 } 6246 e.dataTransfer.setDragImage(img, 0, 0); 6247 if (presto) img.parentNode.removeChild(img); 6248 } 6249 } 6250 6251 function onDragOver(cm, e) { 6252 var pos = posFromMouse(cm, e); 6253 if (!pos) return; 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); 6259 } 6260 removeChildrenAndAdd(cm.display.dragCursor, frag); 6261 } 6262 6263 function clearDragCursor(cm) { 6264 if (cm.display.dragCursor) { 6265 cm.display.lineSpace.removeChild(cm.display.dragCursor); 6266 cm.display.dragCursor = null; 6267 } 6268 } 6269 6270 // SCROLL EVENTS 6271 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); 6282 } 6283 // Sync scroller and scrollbar, ensure the gutter elements are 6284 // aligned. 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); 6292 } 6293 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. 6300 // 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. 6304 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; 6314 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}; 6321 }; 6322 CodeMirror.wheelEventPixels = function(e) { 6323 var delta = wheelEventDelta(e); 6324 delta.x *= wheelPixelsPerUnit; 6325 delta.y *= wheelPixelsPerUnit; 6326 return delta; 6327 }; 6328 6329 function onScrollWheel(cm, e) { 6330 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; 6331 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; 6337 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; 6347 break outer; 6348 } 6349 } 6350 } 6351 } 6352 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 6370 return; 6371 } 6372 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}); 6381 } 6382 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); 6396 ++wheelSamples; 6397 }, 200); 6398 } else { 6399 display.wheelDX += dx; display.wheelDY += dy; 6400 } 6401 } 6402 } 6403 6404 // KEY EVENTS 6405 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; 6411 } 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; 6416 try { 6417 if (cm.isReadOnly()) cm.state.suppressEdits = true; 6418 if (dropShift) cm.display.shift = false; 6419 done = bound(cm) != Pass; 6420 } finally { 6421 cm.display.shift = prevShift; 6422 cm.state.suppressEdits = false; 6423 } 6424 return done; 6425 } 6426 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; 6431 } 6432 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) 6433 || lookupKey(name, cm.options.keyMap, handle, cm); 6434 } 6435 6436 var stopSeq = new Delayed; 6437 function dispatchKey(cm, name, e, handle) { 6438 var seq = cm.state.keySeq; 6439 if (seq) { 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(); 6445 } 6446 }); 6447 name = seq + " " + name; 6448 } 6449 var result = lookupKeyForEditor(cm, name, handle); 6450 6451 if (result == "multi") 6452 cm.state.keySeq = name; 6453 if (result == "handled") 6454 signalLater(cm, "keyHandled", cm, name, e); 6455 6456 if (result == "handled" || result == "multi") { 6457 e_preventDefault(e); 6458 restartBlink(cm); 6459 } 6460 6461 if (seq && !result && /\'$/.test(name)) { 6462 e_preventDefault(e); 6463 return true; 6464 } 6465 return !!result; 6466 } 6467 6468 // Handle a key from the keydown event. 6469 function handleKeyBinding(cm, e) { 6470 var name = keyName(e, true); 6471 if (!name) return false; 6472 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); 6481 }); 6482 } else { 6483 return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); }); 6484 } 6485 } 6486 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); }); 6491 } 6492 6493 var lastStoppedKey = null; 6494 function onKeyDown(e) { 6495 var cm = this; 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); 6503 if (presto) { 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"); 6508 } 6509 6510 // Turn mouse into crosshair when Alt is held on Mac. 6511 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) 6512 showCrossHair(cm); 6513 } 6514 6515 function showCrossHair(cm) { 6516 var lineDiv = cm.display.lineDiv; 6517 addClass(lineDiv, "CodeMirror-crosshair"); 6518 6519 function up(e) { 6520 if (e.keyCode == 18 || !e.altKey) { 6521 rmClass(lineDiv, "CodeMirror-crosshair"); 6522 off(document, "keyup", up); 6523 off(document, "mouseover", up); 6524 } 6525 } 6526 on(document, "keyup", up); 6527 on(document, "mouseover", up); 6528 } 6529 6530 function onKeyUp(e) { 6531 if (e.keyCode == 16) this.doc.sel.shift = false; 6532 signalDOMEvent(this, e); 6533 } 6534 6535 function onKeyPress(e) { 6536 var cm = this; 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); 6544 } 6545 6546 // FOCUS/BLUR EVENTS 6547 6548 function delayBlurEvent(cm) { 6549 cm.state.delayingBlurEvent = true; 6550 setTimeout(function() { 6551 if (cm.state.delayingBlurEvent) { 6552 cm.state.delayingBlurEvent = false; 6553 onBlur(cm); 6554 } 6555 }, 100); 6556 } 6557 6558 function onFocus(cm) { 6559 if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false; 6560 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 6572 } 6573 cm.display.input.receivedFocus(); 6574 } 6575 restartBlink(cm); 6576 } 6577 function onBlur(cm) { 6578 if (cm.state.delayingBlurEvent) return; 6579 6580 if (cm.state.focused) { 6581 signal(cm, "blur", cm); 6582 cm.state.focused = false; 6583 rmClass(cm.display.wrapper, "CodeMirror-focused"); 6584 } 6585 clearInterval(cm.display.blinker); 6586 setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150); 6587 } 6588 6589 // CONTEXT MENU HANDLING 6590 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); 6598 } 6599 6600 function contextMenuInGutter(cm, e) { 6601 if (!hasHandler(cm, "gutterContextMenu")) return false; 6602 return gutterEvent(cm, e, "gutterContextMenu", false); 6603 } 6604 6605 // UPDATING 6606 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)); 6613 }; 6614 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); 6620 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); 6624 } 6625 6626 function computeSelAfterChange(doc, change) { 6627 var out = []; 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))); 6632 } 6633 return normalizeSelection(out, doc.sel.primIndex); 6634 } 6635 6636 function offsetPos(pos, old, nw) { 6637 if (pos.line == old.line) 6638 return Pos(nw.line, pos.ch - old.ch + nw.ch); 6639 else 6640 return Pos(nw.line + (pos.line - old.line), pos.ch); 6641 } 6642 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) { 6646 var out = []; 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; 6653 newPrev = 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); 6657 } else { 6658 out[i] = new Range(from, from); 6659 } 6660 } 6661 return new Selection(out, doc.sel.primIndex); 6662 } 6663 6664 // Allow "beforeChange" event handlers to influence a change 6665 function filterChange(doc, change, update) { 6666 var obj = { 6667 canceled: false, 6668 from: change.from, 6669 to: change.to, 6670 text: change.text, 6671 origin: change.origin, 6672 cancel: function() { this.canceled = true; } 6673 }; 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; 6679 }; 6680 signal(doc, "beforeChange", doc, obj); 6681 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); 6682 6683 if (obj.canceled) return null; 6684 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}; 6685 } 6686 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) { 6690 if (doc.cm) { 6691 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly); 6692 if (doc.cm.state.suppressEdits) return; 6693 } 6694 6695 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { 6696 change = filterChange(doc, change, true); 6697 if (!change) return; 6698 } 6699 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); 6703 if (split) { 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}); 6706 } else { 6707 makeChangeInner(doc, change); 6708 } 6709 } 6710 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); 6715 6716 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); 6717 var rebased = []; 6718 6719 linkedDocs(doc, function(doc, sharedHist) { 6720 if (!sharedHist && indexOf(rebased, doc.history) == -1) { 6721 rebaseHist(doc.history, change); 6722 rebased.push(doc.history); 6723 } 6724 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); 6725 }); 6726 } 6727 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; 6731 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; 6734 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++) { 6738 event = source[i]; 6739 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) 6740 break; 6741 } 6742 if (i == source.length) return; 6743 hist.lastOrigin = hist.lastSelOrigin = null; 6744 6745 for (;;) { 6746 event = source.pop(); 6747 if (event.ranges) { 6748 pushSelectionToHistory(event, dest); 6749 if (allowSelectionOnly && !event.equals(doc.sel)) { 6750 setSelection(doc, event, {clearRedo: false}); 6751 return; 6752 } 6753 selAfter = event; 6754 } 6755 else break; 6756 } 6757 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; 6764 6765 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); 6766 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)) { 6771 source.length = 0; 6772 return; 6773 } 6774 6775 antiChanges.push(historyChangeFromChange(doc, change)); 6776 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)}); 6780 var rebased = []; 6781 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); 6787 } 6788 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); 6789 }); 6790 } 6791 } 6792 6793 // Sub-default 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); 6802 if (doc.cm) { 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"); 6806 } 6807 } 6808 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); 6814 6815 if (change.to.line < doc.first) { 6816 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); 6817 return; 6818 } 6819 if (change.from.line > doc.lastLine()) return; 6820 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}; 6827 } 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}; 6832 } 6833 6834 change.removed = getBetween(doc, change.from, change.to); 6835 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); 6840 } 6841 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; 6846 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; 6853 return true; 6854 } 6855 }); 6856 } 6857 6858 if (doc.sel.contains(change.from, change.to) > -1) 6859 signalCursorActivity(cm); 6860 6861 updateDoc(doc, change, spans, estimateHeight(cm)); 6862 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; 6871 } 6872 }); 6873 if (recomputeMaxLength) cm.curOp.updateMaxLine = true; 6874 } 6875 6876 // Adjust frontier, schedule worker 6877 doc.frontier = Math.min(doc.frontier, from.line); 6878 startWorker(cm, 400); 6879 6880 var lendiff = change.text.length - (to.line - from.line) - 1; 6881 // Remember that these lines changed, for updating the display 6882 if (change.full) 6883 regChange(cm); 6884 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) 6885 regLineChange(cm, from.line, "text"); 6886 else 6887 regChange(cm, from.line, to.line + 1, lendiff); 6888 6889 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); 6890 if (changeHandler || changesHandler) { 6891 var obj = { 6892 from: from, to: to, 6893 text: change.text, 6894 removed: change.removed, 6895 origin: change.origin 6896 }; 6897 if (changeHandler) signalLater(cm, "change", cm, obj); 6898 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); 6899 } 6900 cm.display.selForContextMenu = null; 6901 } 6902 6903 function replaceRange(doc, code, from, to, origin) { 6904 if (!to) to = from; 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}); 6908 } 6909 6910 // SCROLLING THINGS INTO VIEW 6911 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; 6916 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); 6928 } 6929 } 6930 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; 6947 } 6948 if (scrollPos.scrollLeft != null) { 6949 setScrollLeft(cm, scrollPos.scrollLeft); 6950 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true; 6951 } 6952 if (!changed) break; 6953 } 6954 return coords; 6955 } 6956 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); 6962 } 6963 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); 6970 if (y1 < 0) y1 = 0; 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; 6981 } 6982 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; 6987 if (x1 < 10) 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; 6993 return result; 6994 } 6995 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); 7000 if (left != null) 7001 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left; 7002 if (top != null) 7003 cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; 7004 } 7005 7006 // Make sure that at the end of the operation the current cursor is 7007 // shown. 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); 7014 } 7015 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}; 7016 } 7017 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; 7024 if (range) { 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); 7032 } 7033 } 7034 7035 // API UTILITIES 7036 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 7041 // are left alone. 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 7047 // method. 7048 if (!doc.mode.indent) how = "prev"; 7049 else state = getStateBefore(cm, n); 7050 } 7051 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)) { 7057 indentation = 0; 7058 how = "not"; 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; 7063 how = "prev"; 7064 } 7065 } 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; 7075 } 7076 indentation = Math.max(0, indentation); 7077 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); 7082 7083 if (indentString != curSpaceString) { 7084 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); 7085 line.stateAfter = null; 7086 return true; 7087 } else { 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)); 7095 break; 7096 } 7097 } 7098 } 7099 } 7100 7101 // Utility for applying a change to a line by handle or number, 7102 // returning the number and optionally registering the line as 7103 // changed. 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); 7110 return line; 7111 } 7112 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 7118 // ranges. 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; 7125 break; 7126 } 7127 } 7128 kill.push(toKill); 7129 } 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); 7135 }); 7136 } 7137 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() { 7151 var l = line + dir; 7152 if (l < doc.first || l >= doc.first + doc.size) return false 7153 line = l; 7154 return lineObj = getLine(doc, l); 7155 } 7156 function moveOnce(boundToLine) { 7157 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); 7158 if (next == null) { 7159 if (!boundToLine && findNextLine()) { 7160 if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); 7161 else ch = dir < 0 ? lineObj.text.length : 0; 7162 } else return false 7163 } else ch = next; 7164 return true; 7165 } 7166 7167 if (unit == "char") { 7168 moveOnce() 7169 } else if (unit == "column") { 7170 moveOnce(true) 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 7180 : "p"; 7181 if (group && !first && !type) type = "s"; 7182 if (sawType && sawType != type) { 7183 if (dir < 0) {dir = 1; moveOnce();} 7184 break; 7185 } 7186 7187 if (type) sawType = type; 7188 if (dir > 0 && !moveOnce(!first)) break; 7189 } 7190 } 7191 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true); 7192 if (!cmp(pos, result)) result.hitSide = true; 7193 return result; 7194 } 7195 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; 7206 } 7207 for (;;) { 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; } 7211 y += dir * 5; 7212 } 7213 return target; 7214 } 7215 7216 // EDITOR METHODS 7217 7218 // The publicly visible API. Note that methodOp(f) means 7219 // 'wrap f in an operation, performed on its `this` parameter'. 7220 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 7224 // convenience. 7225 7226 CodeMirror.prototype = { 7227 constructor: CodeMirror, 7228 focus: function(){window.focus(); this.display.input.focus();}, 7229 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); 7236 }, 7237 7238 getOption: function(option) {return this.options[option];}, 7239 getDoc: function() {return this.doc;}, 7240 7241 addKeyMap: function(map, bottom) { 7242 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); 7243 }, 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) { 7248 maps.splice(i, 1); 7249 return true; 7250 } 7251 }, 7252 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++; 7258 regChange(this); 7259 }), 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++; 7267 regChange(this); 7268 return; 7269 } 7270 } 7271 }), 7272 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"; 7277 } 7278 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); 7279 }), 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); 7297 } 7298 } 7299 }), 7300 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); 7305 }, 7306 7307 getLineTokens: function(line, precise) { 7308 return takeToken(this, Pos(line), precise, true); 7309 }, 7310 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; 7315 var type; 7316 if (ch == 0) type = styles[2]; 7317 else for (;;) { 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; } 7322 } 7323 var cut = type ? type.indexOf("cm-overlay ") : -1; 7324 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1); 7325 }, 7326 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; 7331 }, 7332 7333 getHelper: function(pos, type) { 7334 return this.getHelpers(pos, type)[0]; 7335 }, 7336 7337 getHelpers: function(pos, type) { 7338 var found = []; 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); 7347 } 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]); 7352 } 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); 7357 } 7358 return found; 7359 }, 7360 7361 getStateAfter: function(line, precise) { 7362 var doc = this.doc; 7363 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); 7364 return getStateBefore(this, line + 1, precise); 7365 }, 7366 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"); 7373 }, 7374 7375 charCoords: function(pos, mode) { 7376 return charCoords(this, clipPos(this.doc, pos), mode || "page"); 7377 }, 7378 7379 coordsChar: function(coords, mode) { 7380 coords = fromCoordSystem(this, coords, mode || "page"); 7381 return coordsChar(this, coords.left, coords.top); 7382 }, 7383 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); 7387 }, 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); 7395 } else { 7396 lineObj = line; 7397 } 7398 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top + 7399 (end ? this.doc.height - heightAtLine(lineObj) : 0); 7400 }, 7401 7402 defaultTextHeight: function() { return textHeight(this.display); }, 7403 defaultCharWidth: function() { return charWidth(this.display); }, 7404 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; 7410 return true; 7411 }); 7412 }), 7413 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; 7421 } 7422 ++i; 7423 }); 7424 }), 7425 7426 lineInfo: function(line) { 7427 if (typeof line == "number") { 7428 if (!isLine(this.doc, line)) return null; 7429 var n = line; 7430 line = getLine(this.doc, line); 7431 if (!line) return null; 7432 } else { 7433 var n = lineNo(line); 7434 if (n == null) return null; 7435 } 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}; 7439 }, 7440 7441 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};}, 7442 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") { 7452 top = pos.top; 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) 7460 top = pos.bottom; 7461 if (left + node.offsetWidth > hspace) 7462 left = hspace - node.offsetWidth; 7463 } 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"; 7469 } else { 7470 if (horiz == "left") left = 0; 7471 else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2; 7472 node.style.left = left + "px"; 7473 } 7474 if (scroll) 7475 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight); 7476 }, 7477 7478 triggerOnKeyDown: methodOp(onKeyDown), 7479 triggerOnKeyPress: methodOp(onKeyPress), 7480 triggerOnKeyUp: onKeyUp, 7481 7482 execCommand: function(cmd) { 7483 if (commands.hasOwnProperty(cmd)) 7484 return commands[cmd].call(null, this); 7485 }, 7486 7487 triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), 7488 7489 findPosH: function(from, amount, unit, visually) { 7490 var dir = 1; 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; 7495 } 7496 return cur; 7497 }, 7498 7499 moveH: methodOp(function(dir, unit) { 7500 var cm = this; 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); 7504 else 7505 return dir < 0 ? range.from() : range.to(); 7506 }, sel_move); 7507 }), 7508 7509 deleteH: methodOp(function(dir, unit) { 7510 var sel = this.doc.sel, doc = this.doc; 7511 if (sel.somethingSelected()) 7512 doc.replaceSelection("", null, "+delete"); 7513 else 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}; 7517 }); 7518 }), 7519 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; 7529 } 7530 return cur; 7531 }, 7532 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) { 7537 if (collapse) 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); 7545 return pos; 7546 }, sel_move); 7547 if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) 7548 doc.sel.ranges[i].goalColumn = goals[i]; 7549 }), 7550 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; 7555 if (line) { 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; 7565 } 7566 return new Range(Pos(pos.line, start), Pos(pos.line, end)); 7567 }, 7568 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"); 7573 else 7574 rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); 7575 7576 signal(this, "overwriteToggle", this, this.state.overwrite); 7577 }, 7578 hasFocus: function() { return this.display.input.getField() == activeElt(); }, 7579 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit); }, 7580 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; 7585 }), 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)}; 7592 }, 7593 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}; 7602 } 7603 if (!range.to) range.to = range.from; 7604 range.margin = margin || 0; 7605 7606 if (range.from.line != null) { 7607 resolveScrollToPos(this); 7608 this.curOp.scrollToPos = range; 7609 } else { 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); 7615 } 7616 }), 7617 7618 setSize: methodOp(function(width, height) { 7619 var cm = this; 7620 function interpret(val) { 7621 return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; 7622 } 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; } 7630 ++lineNo; 7631 }); 7632 cm.curOp.forceUpdate = true; 7633 signal(cm, "refresh", this); 7634 }), 7635 7636 operation: function(f){return runInOp(this, f);}, 7637 7638 refresh: methodOp(function() { 7639 var oldHeight = this.display.cachedTextHeight; 7640 regChange(this); 7641 this.curOp.forceUpdate = true; 7642 clearCaches(this); 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); 7648 }), 7649 7650 swapDoc: methodOp(function(doc) { 7651 var old = this.doc; 7652 old.cm = null; 7653 attachDoc(this, doc); 7654 clearCaches(this); 7655 this.display.input.reset(); 7656 this.scrollTo(doc.scrollLeft, doc.scrollTop); 7657 this.curOp.forceScroll = true; 7658 signalLater(this, "swapDoc", this, old); 7659 return old; 7660 }), 7661 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;} 7666 }; 7667 eventMixin(CodeMirror); 7668 7669 // OPTION DEFAULTS 7670 7671 // The default configuration options. 7672 var defaults = CodeMirror.defaults = {}; 7673 // Functions to run when options are changed. 7674 var optionHandlers = CodeMirror.optionHandlers = {}; 7675 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; 7680 } 7681 7682 // Passed to option handlers when there is no old value. 7683 var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}}; 7684 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) { 7688 cm.setValue(val); 7689 }, true); 7690 option("mode", null, function(cm, val) { 7691 cm.doc.modeOption = val; 7692 loadMode(cm); 7693 }, true); 7694 7695 option("indentUnit", 2, loadMode, true); 7696 option("indentWithTabs", false); 7697 option("smartIndent", true); 7698 option("tabSize", 4, function(cm) { 7699 resetModeState(cm); 7700 clearCaches(cm); 7701 regChange(cm); 7702 }, true); 7703 option("lineSeparator", null, function(cm, val) { 7704 cm.doc.lineSep = val; 7705 if (!val) return; 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)); 7713 } 7714 lineNo++; 7715 }); 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)) 7718 }); 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(); 7722 }); 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 7727 }, true); 7728 option("rtlMoveVisually", !windows); 7729 option("wholeLineUpdateBefore", true); 7730 7731 option("theme", "default", function(cm) { 7732 themeChanged(cm); 7733 guttersChanged(cm); 7734 }, true); 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); 7740 }); 7741 option("extraKeys", null); 7742 7743 option("lineWrapping", false, wrappingChanged, true); 7744 option("gutters", [], function(cm) { 7745 setGuttersForLineNumbers(cm.options); 7746 guttersChanged(cm); 7747 }, true); 7748 option("fixedGutter", true, function(cm, val) { 7749 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; 7750 cm.refresh(); 7751 }, true); 7752 option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true); 7753 option("scrollbarStyle", "native", function(cm) { 7754 initScrollbars(cm); 7755 updateScrollbars(cm); 7756 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); 7757 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); 7758 }, true); 7759 option("lineNumbers", false, function(cm) { 7760 setGuttersForLineNumbers(cm.options); 7761 guttersChanged(cm); 7762 }, true); 7763 option("firstLineNumber", 1, guttersChanged, true); 7764 option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true); 7765 option("showCursorWhenSelecting", false, updateSelection, true); 7766 7767 option("resetSelectionOnContextMenu", true); 7768 option("lineWiseCopyCut", true); 7769 7770 option("readOnly", false, function(cm, val) { 7771 if (val == "nocursor") { 7772 onBlur(cm); 7773 cm.display.input.blur(); 7774 cm.display.disabled = true; 7775 } else { 7776 cm.display.disabled = false; 7777 } 7778 cm.display.input.readOnlyChanged(val) 7779 }); 7780 option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true); 7781 option("dragDrop", true, dragDropChanged); 7782 option("allowDropFileTypes", null); 7783 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(); 7799 }); 7800 7801 option("tabindex", null, function(cm, val) { 7802 cm.display.input.getField().tabIndex = val || ""; 7803 }); 7804 option("autofocus", null); 7805 7806 // MODE DEFINITION AND QUERYING 7807 7808 // Known modes, by name and by MIME 7809 var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; 7810 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); 7818 modes[name] = mode; 7819 }; 7820 7821 CodeMirror.defineMIME = function(mime, spec) { 7822 mimeModes[mime] = spec; 7823 }; 7824 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"); 7837 } 7838 if (typeof spec == "string") return {name: spec}; 7839 else return spec || {name: "null"}; 7840 }; 7841 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]; 7855 } 7856 } 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]; 7861 7862 return modeObj; 7863 }; 7864 7865 // Minimal default mode. 7866 CodeMirror.defineMode("null", function() { 7867 return {token: function(stream) {stream.skipToEnd();}}; 7868 }); 7869 CodeMirror.defineMIME("text/plain", "null"); 7870 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); 7877 }; 7878 7879 // EXTENSIONS 7880 7881 CodeMirror.defineExtension = function(name, func) { 7882 CodeMirror.prototype[name] = func; 7883 }; 7884 CodeMirror.defineDocExtension = function(name, func) { 7885 Doc.prototype[name] = func; 7886 }; 7887 CodeMirror.defineOption = option; 7888 7889 var initHooks = []; 7890 CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; 7891 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; 7896 }; 7897 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { 7898 CodeMirror.registerHelper(type, name, value); 7899 helpers[type]._global.push({pred: predicate, val: value}); 7900 }; 7901 7902 // MODE STATE HANDLING 7903 7904 // Utility functions for working with state. Exported because nested 7905 // modes need to do this for their inner modes. 7906 7907 var copyState = CodeMirror.copyState = function(mode, state) { 7908 if (state === true) return state; 7909 if (mode.copyState) return mode.copyState(state); 7910 var nstate = {}; 7911 for (var n in state) { 7912 var val = state[n]; 7913 if (val instanceof Array) val = val.concat([]); 7914 nstate[n] = val; 7915 } 7916 return nstate; 7917 }; 7918 7919 var startState = CodeMirror.startState = function(mode, a1, a2) { 7920 return mode.startState ? mode.startState(a1, a2) : true; 7921 }; 7922 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; 7929 state = info.state; 7930 mode = info.mode; 7931 } 7932 return info || {mode: mode, state: state}; 7933 }; 7934 7935 // STANDARD COMMANDS 7936 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); 7943 }, 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)}; 7950 else 7951 return {from: range.head, to: Pos(range.head.line, len)}; 7952 } else { 7953 return {from: range.from(), to: range.to()}; 7954 } 7955 }); 7956 }, 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))}; 7961 }); 7962 }, 7963 delLineLeft: function(cm) { 7964 deleteNearSelection(cm, function(range) { 7965 return {from: Pos(range.from().line, 0), to: range.from()}; 7966 }); 7967 }, 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()}; 7973 }); 7974 }, 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 }; 7980 }); 7981 }, 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}); 7991 }, 7992 goLineStartSmart: function(cm) { 7993 cm.extendSelectionsBy(function(range) { 7994 return lineStartSmart(cm, range.head); 7995 }, {origin: "+move", bias: 1}); 7996 }, 7997 goLineEnd: function(cm) { 7998 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, 7999 {origin: "+move", bias: -1}); 8000 }, 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"); 8005 }, sel_move); 8006 }, 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"); 8011 }, sel_move); 8012 }, 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); 8018 return pos; 8019 }, sel_move); 8020 }, 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)); 8049 } 8050 cm.replaceSelections(spaces); 8051 }, 8052 defaultTab: function(cm) { 8053 if (cm.somethingSelected()) cm.indentSelection("add"); 8054 else cm.execCommand("insertTab"); 8055 }, 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; 8061 if (line) { 8062 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); 8063 if (cur.ch > 0) { 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; 8069 if (prev) 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"); 8073 } 8074 } 8075 newSel.push(new Range(cur, cur)); 8076 } 8077 cm.setSelections(newSel); 8078 }); 8079 }, 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); 8087 } 8088 ensureCursorVisible(cm); 8089 }); 8090 }, 8091 openLine: function(cm) {cm.replaceSelection("\n", "start")}, 8092 toggleOverwrite: function(cm) {cm.toggleOverwrite();} 8093 }; 8094 8095 8096 // STANDARD KEYMAPS 8097 8098 var keyMap = CodeMirror.keyMap = {}; 8099 8100 keyMap.basic = { 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" 8107 }; 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" 8120 }; 8121 // Very basic readline/emacs-style bindings, which are standard on Mac. 8122 keyMap.emacsy = { 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" 8128 }; 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"] 8138 }; 8139 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; 8140 8141 // KEYMAP DISPATCH 8142 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++) { 8147 var mod = parts[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); 8153 } 8154 if (alt) name = "Alt-" + name; 8155 if (ctrl) name = "Ctrl-" + name; 8156 if (cmd) name = "Cmd-" + name; 8157 if (shift) name = "Shift-" + name; 8158 return name; 8159 } 8160 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 8165 // this. 8166 CodeMirror.normalizeKeyMap = function(keymap) { 8167 var copy = {}; 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; } 8172 8173 var keys = map(keyname.split(" "), normalizeKeyName); 8174 for (var i = 0; i < keys.length; i++) { 8175 var val, name; 8176 if (i == keys.length - 1) { 8177 name = keys.join(" "); 8178 val = value; 8179 } else { 8180 name = keys.slice(0, i + 1).join(" "); 8181 val = "..."; 8182 } 8183 var prev = copy[name]; 8184 if (!prev) copy[name] = val; 8185 else if (prev != val) throw new Error("Inconsistent bindings for " + name); 8186 } 8187 delete keymap[keyname]; 8188 } 8189 for (var prop in copy) keymap[prop] = copy[prop]; 8190 return keymap; 8191 }; 8192 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"; 8199 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; 8206 } 8207 } 8208 }; 8209 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"; 8215 }; 8216 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; 8226 return name; 8227 }; 8228 8229 function getKeyMap(val) { 8230 return typeof val == "string" ? keyMap[val] : val; 8231 } 8232 8233 // FROMTEXTAREA 8234 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; 8248 } 8249 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; 8256 try { 8257 var wrappedSubmit = form.submit = function() { 8258 save(); 8259 form.submit = realSubmit; 8260 form.submit(); 8261 form.submit = wrappedSubmit; 8262 }; 8263 } catch(e) {} 8264 } 8265 } 8266 8267 options.finishInit = function(cm) { 8268 cm.save = save; 8269 cm.getTextArea = function() { return textarea; }; 8270 cm.toTextArea = function() { 8271 cm.toTextArea = isNaN; // Prevent this from being ran twice 8272 save(); 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; 8279 } 8280 }; 8281 }; 8282 8283 textarea.style.display = "none"; 8284 var cm = CodeMirror(function(node) { 8285 textarea.parentNode.insertBefore(node, textarea.nextSibling); 8286 }, options); 8287 return cm; 8288 }; 8289 8290 // STRING STREAM 8291 8292 // Fed to the mode parsers, provides helper functions to make 8293 // parsers more succinct. 8294 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; 8300 this.lineStart = 0; 8301 }; 8302 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;}, 8307 next: function() { 8308 if (this.pos < this.string.length) 8309 return this.string.charAt(this.pos++); 8310 }, 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;} 8316 }, 8317 eatWhile: function(match) { 8318 var start = this.pos; 8319 while (this.eat(match)){} 8320 return this.pos > start; 8321 }, 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; 8326 }, 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;} 8331 }, 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; 8337 } 8338 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); 8339 }, 8340 indentation: function() { 8341 return countColumn(this.string, null, this.tabSize) - 8342 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); 8343 }, 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; 8350 return true; 8351 } 8352 } else { 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; 8356 return match; 8357 } 8358 }, 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; } 8364 } 8365 }; 8366 8367 // TEXTMARKERS 8368 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. 8378 8379 var nextMarkerId = 0; 8380 8381 var TextMarker = CodeMirror.TextMarker = function(doc, type) { 8382 this.lines = []; 8383 this.type = type; 8384 this.doc = doc; 8385 this.id = ++nextMarkerId; 8386 }; 8387 eventMixin(TextMarker); 8388 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); 8397 } 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"); 8403 else if (cm) { 8404 if (span.to != null) max = lineNo(line); 8405 if (span.from != null) min = lineNo(line); 8406 } 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)); 8410 } 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; 8417 } 8418 } 8419 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); 8426 } 8427 if (cm) signalLater(cm, "markerCleared", cm, this); 8428 if (withOp) endOperation(cm); 8429 if (this.parent) this.parent.clear(); 8430 }; 8431 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; 8439 var from, to; 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; 8446 } 8447 if (span.to != null) { 8448 to = Pos(lineObj ? line : lineNo(line), span.to); 8449 if (side == 1) return to; 8450 } 8451 } 8452 return from && {from: from, to: to}; 8453 }; 8454 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); 8463 if (view) { 8464 clearLineMeasurementCacheFor(view); 8465 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; 8466 } 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; 8472 if (dHeight) 8473 updateLineHeight(line, line.height + dHeight); 8474 } 8475 }); 8476 }; 8477 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); 8483 } 8484 this.lines.push(line); 8485 }; 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); 8491 } 8492 }; 8493 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; 8498 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 8503 // document). 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); 8507 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) 8512 return marker; 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; 8519 } 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; 8525 } 8526 8527 if (marker.addToHistory) 8528 addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); 8529 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)); 8538 ++curLine; 8539 }); 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); 8543 }); 8544 8545 if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); }); 8546 8547 if (marker.readOnly) { 8548 sawReadOnlySpans = true; 8549 if (doc.history.done.length || doc.history.undone.length) 8550 doc.clearHistory(); 8551 } 8552 if (marker.collapsed) { 8553 marker.id = ++nextMarkerId; 8554 marker.atomic = true; 8555 } 8556 if (cm) { 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); 8565 } 8566 return marker; 8567 } 8568 8569 // SHARED TEXTMARKERS 8570 8571 // A shared marker spans multiple linked documents. It is 8572 // implemented as a meta-marker-object controlling multiple normal 8573 // markers. 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; 8579 }; 8580 eventMixin(SharedTextMarker); 8581 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"); 8588 }; 8589 SharedTextMarker.prototype.find = function(side, lineObj) { 8590 return this.primary.find(side, lineObj); 8591 }; 8592 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); 8604 }); 8605 return new SharedTextMarker(markers, primary); 8606 } 8607 8608 function findSharedMarkers(doc) { 8609 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), 8610 function(m) { return m.parent; }); 8611 } 8612 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; 8621 } 8622 } 8623 } 8624 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); 8634 } 8635 } 8636 } 8637 } 8638 8639 // TEXTMARKER SPANS 8640 8641 function MarkedSpan(marker, from, to) { 8642 this.marker = marker; 8643 this.from = from; this.to = to; 8644 } 8645 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; 8651 } 8652 } 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]); 8658 return r; 8659 } 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); 8664 } 8665 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)); 8677 } 8678 } 8679 return nw; 8680 } 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)); 8689 } 8690 } 8691 return nw; 8692 } 8693 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; 8705 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); 8710 8711 // Next, merge those two ends 8712 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); 8713 if (first) { 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; 8721 } 8722 } 8723 } 8724 if (last) { 8725 // Fix up .from in last (or move them into first in case of sameLine) 8726 for (var i = 0; i < last.length; ++i) { 8727 var span = last[i]; 8728 if (span.to != null) span.to += offset; 8729 if (span.from == null) { 8730 var found = getMarkedSpanFor(first, span.marker); 8731 if (!found) { 8732 span.from = offset; 8733 if (sameLine) (first || (first = [])).push(span); 8734 } 8735 } else { 8736 span.from += offset; 8737 if (sameLine) (first || (first = [])).push(span); 8738 } 8739 } 8740 } 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); 8744 8745 var newMarkers = [first]; 8746 if (!sameLine) { 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); 8756 } 8757 return newMarkers; 8758 } 8759 8760 // Remove spans that are empty and don't have a clearWhenEmpty 8761 // option of false. 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); 8767 } 8768 if (!spans.length) return null; 8769 return spans; 8770 } 8771 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; 8781 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; 8789 oldCur.push(span); 8790 } 8791 } else if (stretchCur) { 8792 old[i] = stretchCur; 8793 } 8794 } 8795 return old; 8796 } 8797 8798 // Used to 'clip' out readOnly ranges when making a change. 8799 function removeReadOnlyRanges(doc, from, to) { 8800 var markers = null; 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); 8806 } 8807 }); 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) { 8813 var p = parts[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; 8822 } 8823 } 8824 return parts; 8825 } 8826 8827 // Connect or disconnect spans from a line. 8828 function detachMarkedSpans(line) { 8829 var spans = line.markedSpans; 8830 if (!spans) return; 8831 for (var i = 0; i < spans.length; ++i) 8832 spans[i].marker.detachLine(line); 8833 line.markedSpans = null; 8834 } 8835 function attachMarkedSpans(line, spans) { 8836 if (!spans) return; 8837 for (var i = 0; i < spans.length; ++i) 8838 spans[i].marker.attachLine(line); 8839 line.markedSpans = spans; 8840 } 8841 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; } 8846 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; 8858 return b.id - a.id; 8859 } 8860 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) { 8866 sp = sps[i]; 8867 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && 8868 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) 8869 found = sp.marker; 8870 } 8871 return found; 8872 } 8873 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); } 8874 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); } 8875 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) { 8883 var sp = sps[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)) 8891 return true; 8892 } 8893 } 8894 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) { 8900 var merged; 8901 while (merged = collapsedSpanAtStart(line)) 8902 line = merged.find(-1, true).line; 8903 return line; 8904 } 8905 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) { 8909 var merged, lines; 8910 while (merged = collapsedSpanAtEnd(line)) { 8911 line = merged.find(1, true).line; 8912 (lines || (lines = [])).push(line); 8913 } 8914 return lines; 8915 } 8916 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; 8922 return lineNo(vis); 8923 } 8924 // Get the line number of the start of the next visual line after 8925 // the given line. 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; 8933 } 8934 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) { 8941 sp = sps[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)) 8946 return true; 8947 } 8948 } 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)); 8953 } 8954 if (span.marker.inclusiveRight && span.to == line.text.length) 8955 return true; 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; 8962 } 8963 } 8964 8965 // LINE WIDGETS 8966 8967 // Line widgets are block elements displayed above or below a line. 8968 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]; 8972 this.doc = doc; 8973 this.node = node; 8974 }; 8975 eventMixin(LineWidget); 8976 8977 function adjustScrollWhenAboveVisible(cm, line, diff) { 8978 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) 8979 addToScrollPos(cm, null, diff); 8980 } 8981 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"); 8992 }); 8993 }; 8994 LineWidget.prototype.changed = function() { 8995 var oldH = this.height, cm = this.doc.cm, line = this.line; 8996 this.height = null; 8997 var diff = widgetHeight(this) - oldH; 8998 if (!diff) return; 8999 updateLineHeight(line, line.height + diff); 9000 if (cm) runInOp(cm, function() { 9001 cm.curOp.forceUpdate = true; 9002 adjustScrollWhenAboveVisible(cm, line, diff); 9003 }); 9004 }; 9005 9006 function widgetHeight(widget) { 9007 if (widget.height != null) return widget.height; 9008 var cm = widget.doc.cm; 9009 if (!cm) return 0; 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)); 9017 } 9018 return widget.height = widget.node.parentNode.offsetHeight; 9019 } 9020 9021 function addLineWidget(doc, handle, node, options) { 9022 var widget = new LineWidget(doc, node, options); 9023 var cm = doc.cm; 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); 9029 widget.line = line; 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; 9035 } 9036 return true; 9037 }); 9038 return widget; 9039 } 9040 9041 // LINE DATA STRUCTURE 9042 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) { 9046 this.text = text; 9047 attachMarkedSpans(this, markedSpans); 9048 this.height = estimateHeight ? estimateHeight(this) : 1; 9049 }; 9050 eventMixin(Line); 9051 Line.prototype.lineNo = function() { return lineNo(this); }; 9052 9053 // Change the content (text, markers) of a line. Automatically 9054 // invalidates cached information and tries to re-estimate the 9055 // line's height. 9056 function updateLine(line, text, markedSpans, estimateHeight) { 9057 line.text = text; 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); 9065 } 9066 9067 // Detach a line from the document tree and its markers. 9068 function cleanUpLine(line) { 9069 line.parent = null; 9070 detachMarkedSpans(line); 9071 } 9072 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]; 9083 } 9084 return type; 9085 } 9086 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); 9092 } 9093 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; 9099 } 9100 throw new Error("Mode " + mode.name + " failed to advance stream."); 9101 } 9102 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}; 9110 } 9111 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)); 9121 } 9122 return asArray ? tokens : getObj(); 9123 } 9124 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; 9138 style = null; 9139 } else { 9140 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses); 9141 } 9142 if (inner) { 9143 var mName = inner[0].name; 9144 if (mName) style = "m-" + (style ? mName + " " + style : mName); 9145 } 9146 if (!flattenSpans || curStyle != style) { 9147 while (curStart < stream.start) { 9148 curStart = Math.min(stream.start, curStart + 50000); 9149 f(curStart, curStyle); 9150 } 9151 curStyle = style; 9152 } 9153 stream.start = stream.pos; 9154 } 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); 9158 f(pos, curStyle); 9159 curStart = pos; 9160 } 9161 } 9162 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 9166 // line. 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); 9175 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) { 9180 var start = i; 9181 // Ensure there's a token end at the current position, and that i points at it 9182 while (at < end) { 9183 var i_end = st[i]; 9184 if (i_end > end) 9185 st.splice(i, 1, end, st[i+1], i_end); 9186 i += 2; 9187 at = Math.min(end, i_end); 9188 } 9189 if (!style) return; 9190 if (overlay.opaque) { 9191 st.splice(start, i - start, end, "cm-overlay " + style); 9192 i = start + 2; 9193 } else { 9194 for (; start < i; start += 2) { 9195 var cur = st[start+1]; 9196 st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style; 9197 } 9198 } 9199 }, lineClasses); 9200 } 9201 9202 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}; 9203 } 9204 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++; 9214 } 9215 return line.styles; 9216 } 9217 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; 9229 } 9230 } 9231 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-$&")); 9241 } 9242 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 = {}; 9257 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; 9261 builder.pos = 0; 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); 9267 builder.map = []; 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 || ""); 9275 } 9276 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))); 9280 9281 // Store the map and a cache object for the current logical line 9282 if (i == 0) { 9283 lineView.measure.map = builder.map; 9284 lineView.measure.cache = {}; 9285 } else { 9286 (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map); 9287 (lineView.measure.caches || (lineView.measure.caches = [])).push({}); 9288 } 9289 } 9290 9291 // See issue #2901 9292 if (webkit) { 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"; 9296 } 9297 9298 signal(cm, "renderLine", cm, lineView.line, builder.pre); 9299 if (builder.pre.className) 9300 builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); 9301 9302 return builder; 9303 } 9304 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); 9309 return token; 9310 } 9311 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) { 9315 if (!text) return; 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; 9324 } else { 9325 var content = document.createDocumentFragment(), pos = 0; 9326 while (true) { 9327 special.lastIndex = pos; 9328 var m = special.exec(text); 9329 var skipped = m ? m.index - pos : text.length - pos; 9330 if (skipped) { 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; 9337 } 9338 if (!m) break; 9339 pos += skipped + 1; 9340 if (m[0] == "\t") { 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]); 9349 builder.col += 1; 9350 } else { 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); 9355 builder.col += 1; 9356 } 9357 builder.map.push(builder.pos, builder.pos + 1, txt); 9358 builder.pos++; 9359 } 9360 } 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); 9368 } 9369 builder.content.appendChild(content); 9370 } 9371 9372 function splitSpaces(old) { 9373 var out = " "; 9374 for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0"; 9375 out += " "; 9376 return out; 9377 } 9378 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; 9385 for (;;) { 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; 9390 } 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); 9393 startStyle = null; 9394 text = text.slice(part.to - start); 9395 start = part.to; 9396 } 9397 }; 9398 } 9399 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) { 9404 if (!widget) 9405 widget = builder.content.appendChild(document.createElement("span")); 9406 widget.setAttribute("cm-marker", marker.id); 9407 } 9408 if (widget) { 9409 builder.cm.display.input.setUneditable(widget); 9410 builder.content.appendChild(widget); 9411 } 9412 builder.pos += size; 9413 } 9414 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; 9419 if (!spans) { 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)); 9422 return; 9423 } 9424 9425 var len = allText.length, pos = 0, i = 1, text = "", style, css; 9426 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; 9427 for (;;) { 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) { 9438 nextChange = sp.to; 9439 spanEndStyle = ""; 9440 } 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)) 9447 collapsed = sp; 9448 } else if (sp.from > pos && nextChange > sp.from) { 9449 nextChange = sp.from; 9450 } 9451 } 9452 if (endStyles) for (var j = 0; j < endStyles.length; j += 2) 9453 if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] 9454 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; 9462 } 9463 } 9464 if (pos >= len) break; 9465 9466 var upto = Math.min(len, nextChange); 9467 while (true) { 9468 if (text) { 9469 var end = pos + text.length; 9470 if (!collapsed) { 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); 9474 } 9475 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} 9476 pos = end; 9477 spanStartStyle = ""; 9478 } 9479 text = allText.slice(at, at = styles[i++]); 9480 style = interpretTokenStyle(styles[i++], builder.cm.options); 9481 } 9482 } 9483 } 9484 9485 // DOCUMENT DATA STRUCTURE 9486 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); 9493 } 9494 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); 9501 } 9502 function linesFor(start, end) { 9503 for (var i = start, result = []; i < end; ++i) 9504 result.push(new Line(text[i], spansFor(i), estimateHeight)); 9505 return result; 9506 } 9507 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; 9511 9512 // Adjust the line structure 9513 if (change.full) { 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); 9526 } else { 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); 9531 } 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); 9535 } else { 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); 9541 } 9542 9543 signalLater(doc, "change", doc, change); 9544 } 9545 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). 9551 // 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. 9556 // 9557 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html 9558 9559 function LeafChunk(lines) { 9560 this.lines = lines; 9561 this.parent = null; 9562 for (var i = 0, height = 0; i < lines.length; ++i) { 9563 lines[i].parent = this; 9564 height += lines[i].height; 9565 } 9566 this.height = height; 9567 } 9568 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; 9576 cleanUpLine(line); 9577 signalLater(line, "delete"); 9578 } 9579 this.lines.splice(at, n); 9580 }, 9581 // Helper used to collapse a small branch into a single leaf. 9582 collapse: function(lines) { 9583 lines.push.apply(lines, this.lines); 9584 }, 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; 9591 }, 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; 9596 } 9597 }; 9598 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; 9605 ch.parent = this; 9606 } 9607 this.size = size; 9608 this.height = height; 9609 this.parent = null; 9610 } 9611 9612 BranchChunk.prototype = { 9613 chunkSize: function() { return this.size; }, 9614 removeInner: function(at, n) { 9615 this.size -= n; 9616 for (var i = 0; i < this.children.length; ++i) { 9617 var child = this.children[i], sz = child.chunkSize(); 9618 if (at < sz) { 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; 9624 at = 0; 9625 } else at -= sz; 9626 } 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))) { 9631 var lines = []; 9632 this.collapse(lines); 9633 this.children = [new LeafChunk(lines)]; 9634 this.children[0].parent = this; 9635 } 9636 }, 9637 collapse: function(lines) { 9638 for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines); 9639 }, 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(); 9645 if (at <= sz) { 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); 9655 leaf.parent = this; 9656 } 9657 child.lines = child.lines.slice(0, remaining); 9658 this.maybeSpill(); 9659 } 9660 break; 9661 } 9662 at -= sz; 9663 } 9664 }, 9665 // When a node has grown, check whether it should be split. 9666 maybeSpill: function() { 9667 if (this.children.length <= 10) return; 9668 var me = this; 9669 do { 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); 9674 copy.parent = me; 9675 me.children = [copy, sibling]; 9676 me = copy; 9677 } else { 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); 9682 } 9683 sibling.parent = me.parent; 9684 } while (me.children.length > 10); 9685 me.parent.maybeSpill(); 9686 }, 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(); 9690 if (at < sz) { 9691 var used = Math.min(n, sz - at); 9692 if (child.iterN(at, used, op)) return true; 9693 if ((n -= used) == 0) break; 9694 at = 0; 9695 } else at -= sz; 9696 } 9697 } 9698 }; 9699 9700 var nextDocId = 0; 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; 9704 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; 9718 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); 9722 }; 9723 9724 Doc.prototype = createObj(BranchChunk.prototype, { 9725 constructor: Doc, 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); 9733 }, 9734 9735 // Non-public interface for adding and removing lines. 9736 insert: function(at, lines) { 9737 var height = 0; 9738 for (var i = 0; i < lines.length; ++i) height += lines[i].height; 9739 this.insertInner(at - this.first, lines, height); 9740 }, 9741 remove: function(at, n) { this.removeInner(at - this.first, n); }, 9742 9743 // From here, the methods are part of the public interface. Most 9744 // are also available from CodeMirror (editor) instances. 9745 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()); 9750 }, 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)); 9756 }), 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); 9761 }, 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()); 9766 }, 9767 9768 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, 9769 9770 getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, 9771 getLineNumber: function(line) {return lineNo(line);}, 9772 9773 getLineHandleVisualStart: function(line) { 9774 if (typeof line == "number") line = getLine(this, line); 9775 return visualLine(line); 9776 }, 9777 9778 lineCount: function() {return this.size;}, 9779 firstLine: function() {return this.first;}, 9780 lastLine: function() {return this.first + this.size - 1;}, 9781 9782 clipPos: function(pos) {return clipPos(this, pos);}, 9783 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(); 9790 return pos; 9791 }, 9792 listSelections: function() { return this.sel.ranges; }, 9793 somethingSelected: function() {return this.sel.somethingSelected();}, 9794 9795 setCursor: docMethodOp(function(line, ch, options) { 9796 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); 9797 }), 9798 setSelection: docMethodOp(function(anchor, head, options) { 9799 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); 9800 }), 9801 extendSelection: docMethodOp(function(head, other, options) { 9802 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); 9803 }), 9804 extendSelections: docMethodOp(function(heads, options) { 9805 extendSelections(this, clipPosArray(this, heads), options); 9806 }), 9807 extendSelectionsBy: docMethodOp(function(f, options) { 9808 var heads = map(this.sel.ranges, f); 9809 extendSelections(this, clipPosArray(this, heads), options); 9810 }), 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); 9818 }), 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); 9823 }), 9824 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; 9830 } 9831 if (lineSep === false) return lines; 9832 else return lines.join(lineSep || this.lineSeparator()); 9833 }, 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()); 9839 parts[i] = sel; 9840 } 9841 return parts; 9842 }, 9843 replaceSelection: function(code, collapse, origin) { 9844 var dup = []; 9845 for (var i = 0; i < this.sel.ranges.length; i++) 9846 dup[i] = code; 9847 this.replaceSelections(dup, collapse, origin || "+input"); 9848 }, 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}; 9854 } 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); 9860 }), 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);}), 9865 9866 setExtending: function(val) {this.extend = val;}, 9867 getExtending: function() {return this.extend;}, 9868 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}; 9874 }, 9875 clearHistory: function() {this.history = new History(this.history.maxGeneration);}, 9876 9877 markClean: function() { 9878 this.cleanGeneration = this.changeGeneration(true); 9879 }, 9880 changeGeneration: function(forceSplit) { 9881 if (forceSplit) 9882 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; 9883 return this.history.generation; 9884 }, 9885 isClean: function (gen) { 9886 return this.history.generation == (gen || this.cleanGeneration); 9887 }, 9888 9889 getHistory: function() { 9890 return {done: copyHistoryArray(this.history.done), 9891 undone: copyHistoryArray(this.history.undone)}; 9892 }, 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); 9897 }, 9898 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; 9907 return true; 9908 }); 9909 }), 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; 9918 else { 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; 9923 } 9924 return true; 9925 }); 9926 }), 9927 9928 addLineWidget: docMethodOp(function(handle, node, options) { 9929 return addLineWidget(this, handle, node, options); 9930 }), 9931 removeLineWidget: function(widget) { widget.clear(); }, 9932 9933 markText: function(from, to, options) { 9934 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range"); 9935 }, 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"); 9943 }, 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); 9952 } 9953 return markers; 9954 }, 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); 9967 } 9968 ++lineNo; 9969 }); 9970 return found; 9971 }, 9972 getAllMarks: function() { 9973 var markers = []; 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); 9978 }); 9979 return markers; 9980 }, 9981 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; } 9987 off -= sz; 9988 ++lineNo; 9989 }); 9990 return clipPos(this, Pos(lineNo, ch)); 9991 }, 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; 9999 }); 10000 return index; 10001 }, 10002 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; 10009 if (copyHistory) { 10010 doc.history.undoDepth = this.history.undoDepth; 10011 doc.setHistory(this.getHistory()); 10012 } 10013 return doc; 10014 }, 10015 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)); 10026 return copy; 10027 }, 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)); 10036 break; 10037 } 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); 10045 } 10046 }, 10047 iterLinkedDocs: function(f) {linkedDocs(this, f);}, 10048 10049 getMode: function() {return this.mode;}, 10050 getEditor: function() {return this.cm;}, 10051 10052 splitLines: function(str) { 10053 if (this.lineSep) return str.split(this.lineSep); 10054 return splitLinesAuto(str); 10055 }, 10056 lineSeparator: function() { return this.lineSep || "\n"; } 10057 }); 10058 10059 // Public alias. 10060 Doc.prototype.eachLine = Doc.prototype.iter; 10061 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]); 10068 10069 eventMixin(Doc); 10070 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); 10081 } 10082 } 10083 propagate(doc, null, true); 10084 } 10085 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."); 10089 cm.doc = doc; 10090 doc.cm = cm; 10091 estimateLineHeights(cm); 10092 loadMode(cm); 10093 if (!cm.options.lineWrapping) findMaxLine(cm); 10094 cm.options.mode = doc.modeOption; 10095 regChange(cm); 10096 } 10097 10098 // LINE UTILITIES 10099 10100 // Find the line object corresponding to the given line number. 10101 function getLine(doc, n) { 10102 n -= doc.first; 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; } 10108 n -= sz; 10109 } 10110 } 10111 return chunk.lines[n]; 10112 } 10113 10114 // Get the part of a document between two positions, as an array of 10115 // strings. 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); 10122 out.push(text); 10123 ++n; 10124 }); 10125 return out; 10126 } 10127 // Get the lines between from and to, as array of strings. 10128 function getLines(doc, from, to) { 10129 var out = []; 10130 doc.iter(from, to, function(line) { out.push(line.text); }); 10131 return out; 10132 } 10133 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; 10139 } 10140 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(); 10150 } 10151 } 10152 return no + cur.first; 10153 } 10154 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; 10159 outer: do { 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; } 10163 h -= ch; 10164 n += child.chunkSize(); 10165 } 10166 return n; 10167 } while (!chunk.lines); 10168 for (var i = 0; i < chunk.lines.length; ++i) { 10169 var line = chunk.lines[i], lh = line.height; 10170 if (h < lh) break; 10171 h -= lh; 10172 } 10173 return n + i; 10174 } 10175 10176 10177 // Find the height above the given line. 10178 function heightAtLine(lineObj) { 10179 lineObj = visualLine(lineObj); 10180 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; 10186 } 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; 10192 } 10193 } 10194 return h; 10195 } 10196 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); 10203 return order; 10204 } 10205 10206 // HISTORY 10207 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 10215 // event 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; 10221 } 10222 10223 // Create a history change event from an updateDoc-style change 10224 // object. 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); 10229 return histChange; 10230 } 10231 10232 // Pop all selection events off the end of a history array. Stop at 10233 // a change event. 10234 function clearSelectionEvents(array) { 10235 while (array.length) { 10236 var last = lst(array); 10237 if (last.ranges) array.pop(); 10238 else break; 10239 } 10240 } 10241 10242 // Find the top change event in the history. Pop off selection 10243 // events that are in the way. 10244 function lastChangeEvent(hist, force) { 10245 if (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) { 10251 hist.done.pop(); 10252 return lst(hist.done); 10253 } 10254 } 10255 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; 10263 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); 10275 } else { 10276 // Add new sub-event 10277 cur.changes.push(historyChangeFromChange(doc, change)); 10278 } 10279 } else { 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) { 10288 hist.done.shift(); 10289 if (!hist.done[0].ranges) hist.done.shift(); 10290 } 10291 } 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; 10297 10298 if (!last) signal(doc, "historyAdded"); 10299 } 10300 10301 function selectionEventCanBeMerged(doc, origin, prev, sel) { 10302 var ch = origin.charAt(0); 10303 return ch == "*" || 10304 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); 10308 } 10309 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; 10316 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; 10326 else 10327 pushSelectionToHistory(sel, hist.done); 10328 10329 hist.lastSelTime = +new Date; 10330 hist.lastSelOrigin = origin; 10331 hist.lastSelOp = opId; 10332 if (options && options.clearRedo !== false) 10333 clearSelectionEvents(hist.undone); 10334 } 10335 10336 function pushSelectionToHistory(sel, dest) { 10337 var top = lst(dest); 10338 if (!(top && top.ranges && top.equals(sel))) 10339 dest.push(sel); 10340 } 10341 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; 10348 ++n; 10349 }); 10350 } 10351 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]); 10359 } 10360 return !out ? spans : out.length ? out : null; 10361 } 10362 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])); 10369 return nw; 10370 } 10371 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); 10379 continue; 10380 } 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]; 10390 } 10391 } 10392 } 10393 } 10394 return copy; 10395 } 10396 10397 // Rebasing/resetting history to deal with externally-sourced changes 10398 10399 function rebaseHistSelSingle(pos, from, to, diff) { 10400 if (to < pos.line) { 10401 pos.line += diff; 10402 } else if (from < pos.line) { 10403 pos.line = from; 10404 pos.ch = 0; 10405 } 10406 } 10407 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; 10418 if (sub.ranges) { 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); 10423 } 10424 continue; 10425 } 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) { 10432 ok = false; 10433 break; 10434 } 10435 } 10436 if (!ok) { 10437 array.splice(0, i + 1); 10438 i = 0; 10439 } 10440 } 10441 } 10442 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); 10447 } 10448 10449 // EVENT UTILITIES 10450 10451 // Due to the fact that we still support jurassic IE versions, some 10452 // compatibility wrappers are needed. 10453 10454 var e_preventDefault = CodeMirror.e_preventDefault = function(e) { 10455 if (e.preventDefault) e.preventDefault(); 10456 else e.returnValue = false; 10457 }; 10458 var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) { 10459 if (e.stopPropagation) e.stopPropagation(); 10460 else e.cancelBubble = true; 10461 }; 10462 function e_defaultPrevented(e) { 10463 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false; 10464 } 10465 var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);}; 10466 10467 function e_target(e) {return e.target || e.srcElement;} 10468 function e_button(e) { 10469 var b = e.which; 10470 if (b == null) { 10471 if (e.button & 1) b = 1; 10472 else if (e.button & 2) b = 3; 10473 else if (e.button & 4) b = 2; 10474 } 10475 if (mac && e.ctrlKey && b == 1) b = 3; 10476 return b; 10477 } 10478 10479 // EVENT HANDLING 10480 10481 // Lightweight event framework. on/off also work on DOM nodes, 10482 // registering native DOM handlers. 10483 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); 10489 else { 10490 var map = emitter._handlers || (emitter._handlers = {}); 10491 var arr = map[type] || (map[type] = []); 10492 arr.push(f); 10493 } 10494 }; 10495 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 10501 } 10502 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); 10508 else { 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; } 10512 } 10513 }; 10514 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); 10520 }; 10521 10522 var orphanDelayedCallbacks = null; 10523 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; 10539 } else { 10540 list = orphanDelayedCallbacks = []; 10541 setTimeout(fireOrphanDelayed, 0); 10542 } 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])); 10546 } 10547 10548 function fireOrphanDelayed() { 10549 var delayed = orphanDelayedCallbacks; 10550 orphanDelayedCallbacks = null; 10551 for (var i = 0; i < delayed.length; ++i) delayed[i](); 10552 } 10553 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; 10562 } 10563 10564 function signalCursorActivity(cm) { 10565 var arr = cm._handlers && cm._handlers.cursorActivity; 10566 if (!arr) return; 10567 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); 10568 for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) 10569 set.push(arr[i]); 10570 } 10571 10572 function hasHandler(emitter, type) { 10573 return getHandlers(emitter, type).length > 0 10574 } 10575 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);}; 10581 } 10582 10583 // MISC UTILITIES 10584 10585 // Number of pixels added to scroller and sizer to hide scrollbar 10586 var scrollerGap = 30; 10587 10588 // Returned or thrown by various protocols to signal 'I'm not 10589 // handling this'. 10590 var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; 10591 10592 // Reused option objects for setSelection & friends 10593 var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; 10594 10595 function Delayed() {this.id = null;} 10596 Delayed.prototype.set = function(ms, f) { 10597 clearTimeout(this.id); 10598 this.id = setTimeout(f, ms); 10599 }; 10600 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) { 10604 if (end == null) { 10605 end = string.search(/[^\s\u00a0]/); 10606 if (end == -1) end = string.length; 10607 } 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); 10612 n += nextTab - i; 10613 n += tabSize - (n % tabSize); 10614 i = nextTab + 1; 10615 } 10616 }; 10617 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); 10629 pos = nextTab + 1; 10630 if (col >= goal) return pos; 10631 } 10632 } 10633 10634 var spaceStrs = [""]; 10635 function spaceStr(n) { 10636 while (spaceStrs.length <= n) 10637 spaceStrs.push(lst(spaceStrs) + " "); 10638 return spaceStrs[n]; 10639 } 10640 10641 function lst(arr) { return arr[arr.length-1]; } 10642 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) {} }; 10648 10649 function indexOf(array, elt) { 10650 for (var i = 0; i < array.length; ++i) 10651 if (array[i] == elt) return i; 10652 return -1; 10653 } 10654 function map(array, f) { 10655 var out = []; 10656 for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); 10657 return out; 10658 } 10659 10660 function nothing() {} 10661 10662 function createObj(base, props) { 10663 var inst; 10664 if (Object.create) { 10665 inst = Object.create(base); 10666 } else { 10667 nothing.prototype = base; 10668 inst = new nothing(); 10669 } 10670 if (props) copyObj(props, inst); 10671 return inst; 10672 }; 10673 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]; 10679 return target; 10680 } 10681 10682 function bind(f) { 10683 var args = Array.prototype.slice.call(arguments, 1); 10684 return function(){return f.apply(null, args);}; 10685 } 10686 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)); 10691 }; 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); 10696 } 10697 10698 function isEmpty(obj) { 10699 for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; 10700 return true; 10701 } 10702 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); } 10710 10711 // DOM UTILITIES 10712 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]); 10719 return e; 10720 } 10721 10722 var range; 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); 10727 return r; 10728 }; 10729 else range = function(node, start, end) { 10730 var r = document.body.createTextRange(); 10731 try { r.moveToElementText(node.parentNode); } 10732 catch(e) { return r; } 10733 r.collapse(true); 10734 r.moveEnd("character", end); 10735 r.moveStart("character", start); 10736 return r; 10737 }; 10738 10739 function removeChildren(e) { 10740 for (var count = e.childNodes.length; count > 0; --count) 10741 e.removeChild(e.firstChild); 10742 return e; 10743 } 10744 10745 function removeChildrenAndAdd(parent, e) { 10746 return removeChildren(parent).appendChild(e); 10747 } 10748 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); 10754 do { 10755 if (child.nodeType == 11) child = child.host; 10756 if (child == parent) return true; 10757 } while (child = child.parentNode); 10758 }; 10759 10760 function activeElt() { 10761 var activeElement = document.activeElement; 10762 while (activeElement && activeElement.root && activeElement.root.activeElement) 10763 activeElement = activeElement.root.activeElement; 10764 return activeElement; 10765 } 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; } 10771 }; 10772 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); 10777 if (match) { 10778 var after = current.slice(match.index + match[0].length); 10779 node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); 10780 } 10781 }; 10782 var addClass = CodeMirror.addClass = function(node, cls) { 10783 var current = node.className; 10784 if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls; 10785 }; 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]; 10790 return b; 10791 } 10792 10793 // WINDOW-WIDE EVENTS 10794 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. 10798 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; 10804 if (cm) f(cm); 10805 } 10806 } 10807 10808 var globalsRegistered = false; 10809 function ensureGlobalHandlers() { 10810 if (globalsRegistered) return; 10811 registerGlobalHandlers(); 10812 globalsRegistered = true; 10813 } 10814 function registerGlobalHandlers() { 10815 // When the window resizes, we need to refresh active editors. 10816 var resizeTimer; 10817 on(window, "resize", function() { 10818 if (resizeTimer == null) resizeTimer = setTimeout(function() { 10819 resizeTimer = null; 10820 forEachCodeMirror(onResize); 10821 }, 100); 10822 }); 10823 // When the window loses focus, we want to show the editor as blurred 10824 on(window, "blur", function() { 10825 forEachCodeMirror(onBlur); 10826 }); 10827 } 10828 10829 // FEATURE DETECTION 10830 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; 10838 }(); 10839 10840 var zwspSupported; 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); 10847 } 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", ""); 10851 return node; 10852 } 10853 10854 // Feature-detect IE's crummy client rect reporting for bidi text 10855 var badBidiRects; 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); 10863 } 10864 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; 10869 while (pos <= l) { 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"); 10874 if (rt != -1) { 10875 result.push(line.slice(0, rt)); 10876 pos += rt + 1; 10877 } else { 10878 result.push(line); 10879 pos = nl + 1; 10880 } 10881 } 10882 return result; 10883 } : function(string){return string.split(/\r\n?|\n/);}; 10884 10885 var hasSelection = window.getSelection ? function(te) { 10886 try { return te.selectionStart != te.selectionEnd; } 10887 catch(e) { return false; } 10888 } : function(te) { 10889 try {var range = te.ownerDocument.selection.createRange();} 10890 catch(e) {} 10891 if (!range || range.parentElement() != te) return false; 10892 return range.compareEndPoints("StartToEnd", range) != 0; 10893 }; 10894 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"; 10900 })(); 10901 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; 10909 } 10910 10911 // KEY NAMES 10912 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" 10922 }; 10923 (function() { 10924 // Number keys 10925 for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i); 10926 // Alphabetic keys 10927 for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); 10928 // Function keys 10929 for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; 10930 })(); 10931 10932 // BIDI HELPERS 10933 10934 function iterateBidiSections(order, from, to, f) { 10935 if (!order) return f(from, to, "ltr"); 10936 var found = false; 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"); 10941 found = true; 10942 } 10943 } 10944 if (!found) f(from, to, "ltr"); 10945 } 10946 10947 function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } 10948 function bidiRight(part) { return part.level % 2 ? part.from : part.to; } 10949 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)); 10955 } 10956 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); 10964 } 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; 10969 lineN = null; 10970 } 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); 10974 } 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); 10983 } 10984 return start; 10985 } 10986 10987 function compareBidiLevel(order, a, b) { 10988 var linedir = order[0].level; 10989 if (a == linedir) return true; 10990 if (b == linedir) return false; 10991 return a < b; 10992 } 10993 var bidiOther; 10994 function getBidiPartAt(order, pos) { 10995 bidiOther = null; 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) { 11001 found = i; 11002 } else if (compareBidiLevel(order, cur.level, order[found].level)) { 11003 if (cur.from != cur.to) bidiOther = found; 11004 return i; 11005 } else { 11006 if (cur.from != cur.to) bidiOther = i; 11007 return found; 11008 } 11009 } 11010 } 11011 return found; 11012 } 11013 11014 function moveInLine(line, pos, dir, byUnit) { 11015 if (!byUnit) return pos + dir; 11016 do pos += dir; 11017 while (pos > 0 && isExtendingChar(line.text.charAt(pos))); 11018 return pos; 11019 } 11020 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); 11031 11032 for (;;) { 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; 11038 } else { 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); 11043 else 11044 target = moveInLine(line, part.from, 1, byUnit); 11045 } 11046 } 11047 } 11048 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; 11053 } 11054 11055 // Bidirectional ordering algorithm 11056 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm 11057 // that this (partially) implements. 11058 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 11074 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"; 11090 else return "L"; 11091 } 11092 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"; 11097 11098 function BidiSpan(level, from, to) { 11099 this.level = level; 11100 this.from = from; this.to = to; 11101 } 11102 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))); 11108 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; 11116 else prev = type; 11117 } 11118 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 11122 // number. 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"; } 11128 } 11129 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; 11138 prev = type; 11139 } 11140 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 11144 // Neutral. 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; 11152 i = end - 1; 11153 } 11154 } 11155 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; 11163 } 11164 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; 11178 i = end - 1; 11179 } 11180 } 11181 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. 11187 var order = [], m; 11188 for (var i = 0; i < len;) { 11189 if (countsAsLeft.test(types[i])) { 11190 var start = i; 11191 for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} 11192 order.push(new BidiSpan(0, start, i)); 11193 } else { 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)); 11199 var nstart = j; 11200 for (++j; j < i && countsAsNum.test(types[j]); ++j) {} 11201 order.splice(at, 0, new BidiSpan(2, nstart, j)); 11202 pos = j; 11203 } else ++j; 11204 } 11205 if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)); 11206 } 11207 } 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)); 11211 } 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)); 11215 } 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)); 11220 11221 return order; 11222 }; 11223 })(); 11224 11225 // THE END 11226 11227 CodeMirror.version = "5.15.2"; 11228 11229 return CodeMirror; 11230}); 11231 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 11235 11236(function(mod) { 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 11242 mod(CodeMirror); 11243})(function(CodeMirror) { 11244"use strict"; 11245 11246var 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|facet
11246ime|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 11247 11248CodeMirror.defineMode("gfm", function(config, modeConfig) { 11249 var codeDepth = 0; 11250 function blankLine(state) { 11251 state.code = false; 11252 return null; 11253 } 11254 var gfmOverlay = { 11255 startState: function() { 11256 return { 11257 code: false, 11258 codeBlock: false, 11259 ateSpace: false 11260 }; 11261 }, 11262 copyState: function(s) { 11263 return { 11264 code: s.code, 11265 codeBlock: s.codeBlock, 11266 ateSpace: s.ateSpace 11267 }; 11268 }, 11269 token: function(stream, state) { 11270 state.combineTokens = null; 11271 11272 // Hack to prevent formatting override inside code blocks (block and inline) 11273 if (state.codeBlock) { 11274 if (stream.match(/^```+/)) { 11275 state.codeBlock = false; 11276 return null; 11277 } 11278 stream.skipToEnd(); 11279 return null; 11280 } 11281 if (stream.sol()) { 11282 state.code = false; 11283 } 11284 if (stream.sol() && stream.match(/^```+/)) { 11285 stream.skipToEnd(); 11286 state.codeBlock = true; 11287 return null; 11288 } 11289 // If this block is changed, it may need to be updated in Markdown mode 11290 if (stream.peek() === '`') { 11291 stream.next(); 11292 var before = stream.pos; 11293 stream.eatWhile('`'); 11294 var difference = 1 + stream.pos - before; 11295 if (!state.code) { 11296 codeDepth = difference; 11297 state.code = true; 11298 } else { 11299 if (difference === codeDepth) { // Must be exact 11300 state.code = false; 11301 } 11302 } 11303 return null; 11304 } else if (state.code) { 11305 stream.next(); 11306 return null; 11307 } 11308 // Check if space. If so, links can be formatted later on 11309 if (stream.eatSpace()) { 11310 state.ateSpace = true; 11311 return null; 11312 } 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 11318 // User@SHA 11319 // SHA 11320 state.combineTokens = true; 11321 return "link"; 11322 } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) { 11323 // User/Project#Num 11324 // User#Num 11325 // #Num 11326 state.combineTokens = true; 11327 return "link"; 11328 } 11329 } 11330 } 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)))) { 11334 // URLs 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; 11339 return "link"; 11340 } 11341 stream.next(); 11342 return null; 11343 }, 11344 blankLine: blankLine 11345 }; 11346 11347 var markdownConfig = { 11348 underscoresBreakWords: false, 11349 taskLists: true, 11350 fencedCodeBlocks: '```', 11351 strikethrough: true 11352 }; 11353 for (var attr in modeConfig) { 11354 markdownConfig[attr] = modeConfig[attr]; 11355 } 11356 markdownConfig.name = "markdown"; 11357 return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay); 11358 11359}, "markdown"); 11360 11361 CodeMirror.defineMIME("text/x-gfm", "gfm"); 11362}); 11363 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 11367 11368(function(mod) { 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 11374 mod(CodeMirror); 11375})(function(CodeMirror) { 11376"use strict"; 11377 11378CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { 11379 11380 var htmlMode = CodeMirror.getMode(cmCfg, "text/html"); 11381 var htmlModeMissing = htmlMode.name == "null" 11382 11383 function getMode(name) { 11384 if (CodeMirror.findModeByName) { 11385 var found = CodeMirror.findModeByName(name); 11386 if (found) name = found.mime || found.mimes[0]; 11387 } 11388 var mode = CodeMirror.getMode(cmCfg, name); 11389 return mode.name == "null" ? null : mode; 11390 } 11391 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; 11396 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; 11401 11402 // Should underscores in words open/close em/strong? 11403 if (modeCfg.underscoresBreakWords === undefined) 11404 modeCfg.underscoresBreakWords = true; 11405 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). 11410 11411 // Turn on task lists? ("- [ ] " and "- [x] ") 11412 if (modeCfg.taskLists === undefined) modeCfg.taskLists = false; 11413 11414 // Turn on strikethrough syntax 11415 if (modeCfg.strikethrough === undefined) 11416 modeCfg.strikethrough = false; 11417
11418 // Allow token types to be overridden by user-provided token types. 11419 if (modeCfg.tokenTypeOverrides === undefined) 11420 modeCfg.tokenTypeOverrides = {}; 11421 11422 var tokenTypes = { 11423 header: "header", 11424 code: "comment", 11425 quote: "quote", 11426 list1: "variable-2", 11427 list2: "variable-3", 11428 list3: "keyword", 11429 hr: "hr", 11430 image: "tag", 11431 formatting: "formatting", 11432 linkInline: "link", 11433 linkEmail: "link", 11434 linkText: "link", 11435 linkHref: "string", 11436 em: "em", 11437 strong: "strong", 11438 strikethrough: "strikethrough" 11439 }; 11440 11441 for (var tokenType in tokenTypes) { 11442 if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) { 11443 tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType]; 11444 } 11445 } 11446 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+#\-]*)"); 11456 11457 function switchInline(stream, state, f) { 11458 state.f = state.inline = f; 11459 return f(stream, state); 11460 } 11461 11462 function switchBlock(stream, state, f) { 11463 state.f = state.block = f; 11464 return f(stream, state); 11465 } 11466 11467 function lineIsEmpty(line) { 11468 return !line || !/\S/.test(line.string) 11469 } 11470 11471 // Blocks 11472 11473 function blankLine(state) { 11474 // Reset linkTitle state 11475 state.linkTitle = false; 11476 // Reset EM state 11477 state.em = false; 11478 // Reset STRONG state 11479 state.strong = false; 11480 // Reset strikethrough state 11481 state.strikethrough = false; 11482 // Reset state.quote 11483 state.quote = 0; 11484 // Reset state.indentedCode 11485 state.indentedCode = false; 11486 if (htmlModeMissing && state.f == htmlBlock) { 11487 state.f = inlineNormal; 11488 state.block = blockNormal; 11489 } 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 11496 return null; 11497 } 11498 11499 function blockNormal(stream, state) { 11500 11501 var sol = stream.sol(); 11502 11503 var prevLineIsList = state.list !== false, 11504 prevLineIsIndentedCode = state.indentedCode; 11505 11506 state.indentedCode = false; 11507 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; 11512 } 11513 state.list = null; 11514 } else if (state.indentation > 0) { 11515 state.list = null; 11516 } else { // No longer a list 11517 state.list = false; 11518 } 11519 } 11520 11521 var match = null; 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; 11528 } else { 11529 return null; 11530 } 11531 } else if (stream.eatSpace()) { 11532 return null; 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"; 11547 stream.eatSpace(); 11548 return getType(state); 11549 } else if (stream.peek() === '[') { 11550 return switchInline(stream, state, footnoteLink); 11551 } else if (stream.match(hrRE, true)) { 11552 state.hr = 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)) { 11557 listType = 'ul'; 11558 } else { 11559 stream.match(olRE, true); 11560 listType = 'ol'; 11561 } 11562 state.indentation = stream.column() + stream.current().length; 11563 state.list = true; 11564 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(); 11570 } 11571 11572 // Add this list item's content's indentation to the stack 11573 state.listStack.push(state.indentation); 11574 11575 if (modeCfg.taskLists && stream.match(taskListRE, false)) { 11576 state.taskList = true; 11577 } 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"; 11588 state.code = -1 11589 return getType(state); 11590 } 11591 11592 return switchInline(stream, state, state.inline); 11593 } 11594 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; 11605 } 11606 } 11607 return style; 11608 } 11609 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; 11614 return null; 11615 } else if (state.localMode) { 11616 return state.localMode.token(stream, state.localState); 11617 } else { 11618 stream.skipToEnd(); 11619 return tokenTypes.code; 11620 } 11621 } 11622 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"; 11629 state.code = 1 11630 var returnType = getType(state); 11631 state.code = 0 11632 return returnType; 11633 } 11634 11635 // Inline 11636 function getType(state) { 11637 var styles = []; 11638 11639 if (state.formatting) { 11640 styles.push(tokenTypes.formatting); 11641 11642 if (typeof state.formatting === "string") state.formatting = [state.formatting]; 11643 11644 for (var i = 0; i < state.formatting.length; i++) { 11645 styles.push(tokenTypes.formatting + "-" + state.formatting[i]); 11646 11647 if (state.formatting[i] === "header") { 11648 styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header); 11649 } 11650 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); 11656 } else { 11657 styles.push("error"); 11658 } 11659 } 11660 } 11661 } 11662 11663 if (state.taskOpen) { 11664 styles.push("meta"); 11665 return styles.length ? styles.join(' ') : null; 11666 } 11667 if (state.taskClosed) { 11668 styles.push("property"); 11669 return styles.length ? styles.join(' ') : null; 11670 } 11671 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); } 11680 } 11681 11682 if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); } 11683 11684 if (state.quote) { 11685 styles.push(tokenTypes.quote); 11686 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); 11690 } else { 11691 styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth); 11692 } 11693 } 11694 11695 if (state.list !== false) { 11696 var listMod = (state.listStack.length - 1) % 3; 11697 if (!listMod) { 11698 styles.push(tokenTypes.list1); 11699 } else if (listMod === 1) { 11700 styles.push(tokenTypes.list2); 11701 } else { 11702 styles.push(tokenTypes.list3); 11703 } 11704 } 11705 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")); 11710 } 11711 11712 return styles.length ? styles.join(' ') : null; 11713 } 11714 11715 function handleText(stream, state) { 11716 if (stream.match(textRE, true)) { 11717 return getType(state); 11718 } 11719 return undefined; 11720 } 11721 11722 function inlineNormal(stream, state) { 11723 var style = state.text(stream, state); 11724 if (typeof style !== 'undefined') 11725 return style; 11726 11727 if (state.list) { // List marker (*, +, -, 1., etc) 11728 state.list = null; 11729 return getType(state); 11730 } 11731 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); 11739 } 11740 11741 state.taskOpen = false; 11742 state.taskClosed = false; 11743 11744 if (state.header && stream.match(/^#+$/, true)) { 11745 if (modeCfg.highlightFormatting) state.formatting = "header"; 11746 return getType(state); 11747 } 11748 11749 // Get sol() value now, before character is consumed 11750 var sol = stream.sol(); 11751 11752 var ch = stream.next(); 11753 11754 // Matches link titles present on next line 11755 if (state.linkTitle) { 11756 state.linkTitle = false; 11757 var matchCh = ch; 11758 if (ch === '(') { 11759 matchCh = ')'; 11760 } 11761 matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); 11762 var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh; 11763 if (stream.match(new RegExp(regex), true)) { 11764 return tokenTypes.linkHref; 11765 } 11766 } 11767 11768 // If this block is changed, it may need to be updated in GFM mode 11769 if (ch === '`') { 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) { 11775 state.code = count 11776 return getType(state) 11777 } else if (count == state.code) { // Must be exact 11778 var t = getType(state) 11779 state.code = 0 11780 return t 11781 } else { 11782 state.formatting = previousFormatting 11783 return getType(state) 11784 } 11785 } else if (state.code) { 11786 return getType(state); 11787 } 11788 11789 if (ch === '\\') { 11790 stream.next(); 11791 if (modeCfg.highlightFormatting) { 11792 var type = getType(state); 11793 var formattingEscape = tokenTypes.formatting + "-escape"; 11794 return type ? type + " " + formattingEscape : formattingEscape; 11795 } 11796 } 11797 11798 if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) { 11799 stream.match(/\[[^\]]*\]/); 11800 state.inline = state.f = linkHref; 11801 return tokenTypes.image; 11802 } 11803 11804 if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false)) { 11805 state.linkText = true; 11806 if (modeCfg.highlightFormatting) state.formatting = "link"; 11807 return getType(state); 11808 } 11809 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; 11815 return type; 11816 } 11817 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); 11822 if (type){ 11823 type += " "; 11824 } else { 11825 type = ""; 11826 } 11827 return type + tokenTypes.linkInline; 11828 } 11829 11830 if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) { 11831 state.f = state.inline = linkInline; 11832 if (modeCfg.highlightFormatting) state.formatting = "link"; 11833 var type = getType(state); 11834 if (type){ 11835 type += " "; 11836 } else { 11837 type = ""; 11838 } 11839 return type + tokenTypes.linkEmail; 11840 } 11841 11842 if (ch === '<' && stream.match(/^(!--|\w)/, false)) { 11843 var end = stream.string.indexOf(">", stream.pos); 11844 if (end != -1) { 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; 11847 } 11848 stream.backUp(1); 11849 state.htmlState = CodeMirror.startState(htmlMode); 11850 return switchBlock(stream, state, htmlBlock); 11851 } 11852 11853 if (ch === '<' && stream.match(/^\/\w*?>/)) { 11854 state.md_inside = false; 11855 return "tag"; 11856 } 11857 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; 11866 } 11867 } 11868 } 11869 } 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; 11877 return t; 11878 } else if (!state.strong && stream.eat(ch)) { // Add STRONG 11879 state.strong = ch; 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); 11885 state.em = false; 11886 return t; 11887 } else if (!state.em) { // Add EM 11888 state.em = ch; 11889 if (modeCfg.highlightFormatting) state.formatting = "em"; 11890 return getType(state); 11891 } 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 11897 stream.backUp(1); 11898 } 11899 } 11900 } 11901 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; 11908 return t; 11909 } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough 11910 state.strikethrough = true; 11911 if (modeCfg.highlightFormatting) state.formatting = "strikethrough"; 11912 return getType(state); 11913 } 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 11919 stream.backUp(2); 11920 } 11921 } 11922 } 11923 } 11924 11925 if (ch === ' ') { 11926 if (stream.match(/ +$/, false)) { 11927 state.trailingSpace++; 11928 } else if (state.trailingSpace) { 11929 state.trailingSpaceNewLine = true; 11930 } 11931 } 11932 11933 return getType(state); 11934 } 11935 11936 function linkInline(stream, state) { 11937 var ch = stream.next(); 11938 11939 if (ch === ">") { 11940 state.f = state.inline = inlineNormal; 11941 if (modeCfg.highlightFormatting) state.formatting = "link"; 11942 var type = getType(state); 11943 if (type){ 11944 type += " "; 11945 } else { 11946 type = ""; 11947 } 11948 return type + tokenTypes.linkInline; 11949 } 11950 11951 stream.match(/^[^>]+/, true); 11952 11953 return tokenTypes.linkInline; 11954 } 11955 11956 function linkHref(stream, state) { 11957 // Check if space, and return NULL if so (to avoid marking the space) 11958 if(stream.eatSpace()){ 11959 return null; 11960 } 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); 11967 } 11968 return 'error'; 11969 } 11970 11971 var linkRE = { 11972 ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/, 11973 "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\\]]|\\.)*\])*?(?=\])/ 11974 } 11975 11976 function getLinkHrefInside(endChar) { 11977 return function(stream, state) { 11978 var ch = stream.next(); 11979 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; 11986 } 11987 11988 stream.match(linkRE[endChar]) 11989 state.linkHref = true; 11990 return getType(state); 11991 }; 11992 } 11993 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); 12001 } 12002 return switchInline(stream, state, inlineNormal); 12003 } 12004 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; 12011 return returnType; 12012 } 12013 12014 stream.match(/^([^\]\\]|\\.)+/, true); 12015 12016 return tokenTypes.linkText; 12017 } 12018 12019 function footnoteUrl(stream, state) { 12020 // Check if space, and return NULL if so (to avoid marking the space) 12021 if(stream.eatSpace()){ 12022 return null; 12023 }
12024 // Match URL 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); 12031 } 12032 state.f = state.inline = inlineNormal; 12033 return tokenTypes.linkHref + " url"; 12034 } 12035 12036 var mode = { 12037 startState: function() { 12038 return { 12039 f: blockNormal, 12040 12041 prevLine: null, 12042 thisLine: null, 12043 12044 block: blockNormal, 12045 htmlState: null, 12046 indentation: 0, 12047 12048 inline: inlineNormal, 12049 text: handleText, 12050 12051 formatting: false, 12052 linkText: false, 12053 linkHref: false, 12054 linkTitle: false, 12055 code: 0, 12056 em: false, 12057 strong: false, 12058 header: 0, 12059 hr: false, 12060 taskList: false, 12061 list: false, 12062 listStack: [], 12063 quote: 0, 12064 trailingSpace: 0, 12065 trailingSpaceNewLine: false, 12066 strikethrough: false, 12067 fencedChars: null 12068 }; 12069 }, 12070 12071 copyState: function(s) { 12072 return { 12073 f: s.f, 12074 12075 prevLine: s.prevLine, 12076 thisLine: s.thisLine, 12077 12078 block: s.block, 12079 htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState), 12080 indentation: s.indentation, 12081 12082 localMode: s.localMode, 12083 localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null, 12084 12085 inline: s.inline, 12086 text: s.text, 12087 formatting: false, 12088 linkTitle: s.linkTitle, 12089 code: s.code, 12090 em: s.em, 12091 strong: s.strong, 12092 strikethrough: s.strikethrough, 12093 header: s.header, 12094 hr: s.hr, 12095 taskList: s.taskList, 12096 list: s.list, 12097 listStack: s.listStack.slice(0), 12098 quote: s.quote, 12099 indentedCode: s.indentedCode, 12100 trailingSpace: s.trailingSpace, 12101 trailingSpaceNewLine: s.trailingSpaceNewLine, 12102 md_inside: s.md_inside, 12103 fencedChars: s.fencedChars 12104 }; 12105 }, 12106 12107 token: function(stream, state) { 12108 12109 // Reset state.formatting 12110 state.formatting = false; 12111 12112 if (stream != state.thisLine) { 12113 var forceBlankLine = state.header || state.hr; 12114 12115 // Reset state.header and state.hr 12116 state.header = 0; 12117 state.hr = false; 12118 12119 if (stream.match(/^\s*$/, true) || forceBlankLine) { 12120 blankLine(state); 12121 if (!forceBlankLine) return null 12122 state.prevLine = null 12123 } 12124 12125 state.prevLine = state.thisLine 12126 state.thisLine = stream 12127 12128 // Reset state.taskList 12129 state.taskList = false; 12130 12131 // Reset state.trailingSpace 12132 state.trailingSpace = 0; 12133 state.trailingSpaceNewLine = false; 12134 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; 12140 } 12141 return state.f(stream, state); 12142 }, 12143 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}; 12148 }, 12149 12150 blankLine: blankLine, 12151 12152 getType: getType, 12153 12154 fold: "markdown" 12155 }; 12156 return mode; 12157}, "xml"); 12158 12159CodeMirror.defineMIME("text/x-markdown", "markdown"); 12160 12161}); 12162 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 12166 12167(function(mod) { 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 12173 mod(CodeMirror); 12174})(function(CodeMirror) { 12175 "use strict"; 12176 12177 CodeMirror.modeInfo = [ 12178 {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]}
vendor: 10,090 bytes, lines 12178-12296
12178, 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: ["
12296nut"]}, 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"]} 12327 ]; 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]; 12332 } 12333 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; 12341 } 12342 }; 12343 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; 12349 } 12350 }; 12351 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; 12356 } 12357 var dot = filename.lastIndexOf("."); 12358 var ext = dot > -1 && filename.substring(dot + 1, filename.length); 12359 if (ext) return CodeMirror.findModeByExtension(ext); 12360 }; 12361 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; 12369 } 12370 }; 12371}); 12372 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 12376 12377(function(mod) { 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 12383 mod(CodeMirror); 12384})(function(CodeMirror) { 12385"use strict"; 12386 12387var htmlConfig = { 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, 's
vendor: 12,451 bytes, lines 12390-12774
12390ource': 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}, 12395 contextGrabbers: { 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}, 12413 'tr': {'tr': true} 12414 }, 12415 doNotIndent: {"pre": true}, 12416 allowUnquoted: true, 12417 allowMissing: true, 12418 caseFold: true 12419} 12420 12421var xmlConfig = { 12422 autoSelfClosers: {}, 12423 implicitlyClosed: {}, 12424 contextGrabbers: {}, 12425 doNotIndent: {}, 12426 allowUnquoted: false, 12427 allowMissing: false, 12428 caseFold: false 12429} 12430 12431CodeMirror.defineMode("xml", function(editorConf, config_) { 12432 var indentUnit = editorConf.indentUnit 12433 var config = {} 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] 12437 12438 // Return variables for tokenizers 12439 var type, setStyle; 12440 12441 function inText(stream, state) { 12442 function chain(parser) { 12443 state.tokenize = parser; 12444 return parser(stream, state); 12445 } 12446 12447 var ch = stream.next(); 12448 if (ch == "<") { 12449 if (stream.eat("!")) { 12450 if (stream.eat("[")) { 12451 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); 12452 else return null; 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)); 12458 } else { 12459 return null; 12460 } 12461 } else if (stream.eat("?")) { 12462 stream.eatWhile(/[\w\._\-]/); 12463 state.tokenize = inBlock("meta", "?>"); 12464 return "meta"; 12465 } else { 12466 type = stream.eat("/") ? "closeTag" : "openTag"; 12467 state.tokenize = inTag; 12468 return "tag bracket"; 12469 } 12470 } else if (ch == "&") { 12471 var ok; 12472 if (stream.eat("#")) { 12473 if (stream.eat("x")) { 12474 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); 12475 } else { 12476 ok = stream.eatWhile(/[\d]/) && stream.eat(";"); 12477 } 12478 } else { 12479 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); 12480 } 12481 return ok ? "atom" : "error"; 12482 } else { 12483 stream.eatWhile(/[^&<]/); 12484 return null; 12485 } 12486 } 12487 inText.isInText = true; 12488 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 == "=") { 12496 type = "equals"; 12497 return null; 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); 12508 } else { 12509 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); 12510 return "word"; 12511 } 12512 } 12513 12514 function inAttribute(quote) { 12515 var closure = function(stream, state) { 12516 while (!stream.eol()) { 12517 if (stream.next() == quote) { 12518 state.tokenize = inTag; 12519 break; 12520 } 12521 } 12522 return "string"; 12523 }; 12524 closure.isInAttribute = true; 12525 return closure; 12526 } 12527 12528 function inBlock(style, terminator) { 12529 return function(stream, state) { 12530 while (!stream.eol()) { 12531 if (stream.match(terminator)) { 12532 state.tokenize = inText; 12533 break; 12534 } 12535 stream.next(); 12536 } 12537 return style; 12538 }; 12539 } 12540 function doctype(depth) { 12541 return function(stream, state) { 12542 var ch; 12543 while ((ch = stream.next()) != null) { 12544 if (ch == "<") { 12545 state.tokenize = doctype(depth + 1); 12546 return state.tokenize(stream, state); 12547 } else if (ch == ">") { 12548 if (depth == 1) { 12549 state.tokenize = inText; 12550 break; 12551 } else { 12552 state.tokenize = doctype(depth - 1); 12553 return state.tokenize(stream, state); 12554 } 12555 } 12556 } 12557 return "meta"; 12558 }; 12559 } 12560 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; 12568 } 12569 function popContext(state) { 12570 if (state.context) state.context = state.context.prev; 12571 } 12572 function maybePopContext(state, nextTagName) { 12573 var parentTagName; 12574 while (true) { 12575 if (!state.context) { 12576 return; 12577 } 12578 parentTagName = state.context.tagName; 12579 if (!config.contextGrabbers.hasOwnProperty(parentTagName) || 12580 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { 12581 return; 12582 } 12583 popContext(state); 12584 } 12585 } 12586 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; 12593 } else { 12594 return baseState; 12595 } 12596 } 12597 function tagNameState(type, stream, state) { 12598 if (type == "word") { 12599 state.tagName = stream.current(); 12600 setStyle = "tag"; 12601 return attrState; 12602 } else { 12603 setStyle = "error"; 12604 return tagNameState; 12605 } 12606 } 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)) 12612 popContext(state); 12613 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { 12614 setStyle = "tag"; 12615 return closeState; 12616 } else { 12617 setStyle = "tag error"; 12618 return closeStateErr; 12619 } 12620 } else { 12621 setStyle = "error"; 12622 return closeStateErr; 12623 } 12624 } 12625 12626 function closeState(type, _stream, state) { 12627 if (type != "endTag") { 12628 setStyle = "error"; 12629 return closeState; 12630 } 12631 popContext(state); 12632 return baseState; 12633 } 12634 function closeStateErr(type, stream, state) { 12635 setStyle = "error"; 12636 return closeState(type, stream, state); 12637 } 12638 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); 12649 } else { 12650 maybePopContext(state, tagName); 12651 state.context = new Context(state, tagName, tagStart == state.indented); 12652 } 12653 return baseState; 12654 } 12655 setStyle = "error"; 12656 return attrState; 12657 } 12658 function attrEqState(type, stream, state) { 12659 if (type == "equals") return attrValueState; 12660 if (!config.allowMissing) setStyle = "error"; 12661 return attrState(type, stream, state); 12662 } 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); 12668 } 12669 function attrContinuedState(type, stream, state) { 12670 if (type == "string") return attrContinuedState; 12671 return attrState(type, stream, state); 12672 } 12673 12674 return { 12675 startState: function(baseIndent) { 12676 var state = {tokenize: inText, 12677 state: baseState, 12678 indented: baseIndent || 0, 12679 tagName: null, tagStart: null, 12680 context: null} 12681 if (baseIndent != null) state.baseIndent = baseIndent 12682 return state 12683 }, 12684 12685 token: function(stream, state) { 12686 if (!state.tagName && stream.sol()) 12687 state.indented = stream.indentation(); 12688 12689 if (stream.eatSpace()) return null; 12690 type = null; 12691 var style = state.tokenize(stream, state); 12692 if ((style || type) && style != "comment") { 12693 setStyle = null; 12694 state.state = state.state(type || style, stream, state); 12695 if (setStyle) 12696 style = setStyle == "error" ? style + " error" : setStyle; 12697 } 12698 return style; 12699 }, 12700 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; 12707 else 12708 return state.indented + indentUnit; 12709 } 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; 12717 else 12718 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); 12719 } 12720 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0; 12721 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter); 12722 if (tagAfter && tagAfter[1]) { // Closing tag spotted 12723 while (context) { 12724 if (context.tagName == tagAfter[2]) { 12725 context = context.prev; 12726 break; 12727 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) { 12728 context = context.prev; 12729 } else { 12730 break; 12731 } 12732 } 12733 } else if (tagAfter) { // Opening tag spotted 12734 while (context) { 12735 var grabbers = config.contextGrabbers[context.tagName]; 12736 if (grabbers && grabbers.hasOwnProperty(tagAfter[2])) 12737 context = context.prev; 12738 else 12739 break; 12740 } 12741 } 12742 while (context && context.prev && !context.startOfLine) 12743 context = context.prev; 12744 if (context) return context.indent + indentUnit; 12745 else return state.baseIndent || 0; 12746 }, 12747 12748 electricInput: /<\/[\s\w:]+>$/, 12749 blockCommentStart: "<!--", 12750 blockCommentEnd: "-->", 12751 12752 configuration: config.htmlMode ? "html" : "xml", 12753 helperType: config.htmlMode ? "html" : "xml", 12754 12755 skipAttribute: function(state) { 12756 if (state.state == attrValueState) 12757 state.state = attrState 12758 } 12759 }; 12760}); 12761 12762CodeMirror.defineMIME("text/xml", "xml"); 12763CodeMirror.defineMIME("application/xml", "xml"); 12764if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) 12765 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); 12766 12767}); 12768 12769},{"../../lib/codemirror":10}],15:[function(require,module,exports){ 12770exports.read = function (buffer, offset, isLE, mLen, nBytes) { 12771 var e, m 12772 var eLen = nBytes * 8 - mLen - 1 12773 var eMax = (1 << eLen) - 1 12774 var eBias = eMax >> 1
vendor: 4,280 bytes, lines 12775-12941
12775 var nBits = -7 12776 var i = isLE ? (nBytes - 1) : 0 12777 var d = isLE ? -1 : 1 12778 var s = buffer[offset + i] 12779 12780 i += d 12781 12782 e = s & ((1 << (-nBits)) - 1) 12783 s >>= (-nBits) 12784 nBits += eLen 12785 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} 12786 12787 m = e & ((1 << (-nBits)) - 1) 12788 e >>= (-nBits) 12789 nBits += mLen 12790 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} 12791 12792 if (e === 0) { 12793 e = 1 - eBias 12794 } else if (e === eMax) { 12795 return m ? NaN : ((s ? -1 : 1) * Infinity) 12796 } else { 12797 m = m + Math.pow(2, mLen) 12798 e = e - eBias 12799 } 12800 return (s ? -1 : 1) * m * Math.pow(2, e - mLen) 12801} 12802 12803exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { 12804 var e, m, c 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 12812 12813 value = Math.abs(value) 12814 12815 if (isNaN(value) || value === Infinity) { 12816 m = isNaN(value) ? 1 : 0 12817 e = eMax 12818 } else { 12819 e = Math.floor(Math.log(value) / Math.LN2) 12820 if (value * (c = Math.pow(2, -e)) < 1) { 12821 e-- 12822 c *= 2 12823 } 12824 if (e + eBias >= 1) { 12825 value += rt / c 12826 } else { 12827 value += rt * Math.pow(2, 1 - eBias) 12828 } 12829 if (value * c >= 2) { 12830 e++ 12831 c /= 2 12832 } 12833 12834 if (e + eBias >= eMax) { 12835 m = 0 12836 e = eMax 12837 } else if (e + eBias >= 1) { 12838 m = (value * c - 1) * Math.pow(2, mLen) 12839 e = e + eBias 12840 } else { 12841 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) 12842 e = 0 12843 } 12844 } 12845 12846 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} 12847 12848 e = (e << mLen) | m 12849 eLen += mLen 12850 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} 12851 12852 buffer[offset + i - d] |= s * 128 12853} 12854 12855},{}],16:[function(require,module,exports){ 12856var toString = {}.toString; 12857 12858module.exports = Array.isArray || function (arr) { 12859 return toString.call(arr) == '[object Array]'; 12860}; 12861 12862},{}],17:[function(require,module,exports){ 12863(function (global){ 12864/** 12865 * marked - a markdown parser 12866 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) 12867 * https://github.com/chjj/marked 12868 */ 12869 12870;(function() { 12871 12872/** 12873 * Block-Level Grammar 12874 */ 12875 12876var block = { 12877 newline: /^\n+/, 12878 code: /^( {4}[^\n]+\n*)+/, 12879 fences: noop, 12880 hr: /^( *[-*_]){3,} *(?:\n+|$)/, 12881 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, 12882 nptable: noop, 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+|$)/, 12888 table: noop, 12889 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, 12890 text: /^[^\n]+/ 12891}; 12892 12893block.bullet = /(?:[*+-]|\d+\.)/; 12894block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; 12895block.item = replace(block.item, 'gm') 12896 (/bull/g, block.bullet) 12897 (); 12898 12899block.list = replace(block.list) 12900 (/bull/g, block.bullet) 12901 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))') 12902 ('def', '\\n+(?=' + block.def.source + ')') 12903 (); 12904 12905block.blockquote = replace(block.blockquote) 12906 ('def', block.def) 12907 (); 12908 12909block._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'; 12913 12914block.html = replace(block.html) 12915 ('comment', /<!--[\s\S]*?-->/) 12916 ('closed', /<(tag)[\s\S]+?<\/\1>/) 12917 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/) 12918 (/tag/g, block._tag) 12919 (); 12920 12921block.paragraph = replace(block.paragraph) 12922 ('hr', block.hr) 12923 ('heading', block.heading) 12924 ('lheading', block.lheading) 12925 ('blockquote', block.blockquote) 12926 ('tag', '<' + block._tag) 12927 ('def', block.def) 12928 (); 12929 12930/** 12931 * Normal Block Grammar 12932 */ 12933 12934block.normal = merge({}, block); 12935 12936/** 12937 * GFM Block Grammar 12938 */ 12939 12940block.gfm = merge({}, block.normal, { 12941 fences: /^ *(`{3,}|~{3,}
12941)[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/, 12942 paragraph: /^/, 12943 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ 12944}); 12945 12946block.gfm.paragraph = replace(block.paragraph) 12947 ('(?!', '(?!' 12948 + block.gfm.fences.source.replace('\\1', '\\2') + '|' 12949 + block.list.source.replace('\\1', '\\3') + '|') 12950 (); 12951 12952/** 12953 * GFM + Tables Block Grammar 12954 */ 12955 12956block.tables = merge({}, block.gfm, { 12957 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, 12958 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/ 12959}); 12960 12961/** 12962 * Block Lexer 12963 */ 12964 12965function Lexer(options) { 12966 this.tokens = []; 12967 this.tokens.links = {}; 12968 this.options = options || marked.defaults; 12969 this.rules = block.normal; 12970 12971 if (this.options.gfm) { 12972 if (this.options.tables) { 12973 this.rules = block.tables; 12974 } else { 12975 this.rules = block.gfm; 12976 } 12977 } 12978} 12979 12980/** 12981 * Expose Block Rules 12982 */ 12983 12984Lexer.rules = block; 12985 12986/** 12987 * Static Lex Method 12988 */ 12989 12990Lexer.lex = function(src, options) { 12991 var lexer = new Lexer(options); 12992 return lexer.lex(src); 12993}; 12994 12995/** 12996 * Preprocessing 12997 */ 12998 12999Lexer.prototype.lex = function(src) { 13000 src = src 13001 .replace(/\r\n|\r/g, '\n') 13002 .replace(/\t/g, ' ') 13003 .replace(/\u00a0/g, ' ') 13004 .replace(/\u2424/g, '\n'); 13005 13006 return this.token(src, true); 13007}; 13008 13009/** 13010 * Lexing 13011 */ 13012 13013Lexer.prototype.token = function(src, top, bq) { 13014 var src = src.replace(/^ +$/gm, '') 13015 , next 13016 , loose 13017 , cap 13018 , bull 13019 , b 13020 , item 13021 , space 13022 , i 13023 , l; 13024 13025 while (src) { 13026 // newline 13027 if (cap = this.rules.newline.exec(src)) { 13028 src = src.substring(cap[0].length); 13029 if (cap[0].length > 1) { 13030 this.tokens.push({ 13031 type: 'space' 13032 }); 13033 } 13034 } 13035 13036 // code 13037 if (cap = this.rules.code.exec(src)) { 13038 src = src.substring(cap[0].length); 13039 cap = cap[0].replace(/^ {4}/gm, ''); 13040 this.tokens.push({ 13041 type: 'code', 13042 text: !this.options.pedantic 13043 ? cap.replace(/\n+$/, '') 13044 : cap 13045 }); 13046 continue; 13047 } 13048 13049 // fences (gfm) 13050 if (cap = this.rules.fences.exec(src)) { 13051 src = src.substring(cap[0].length); 13052 this.tokens.push({ 13053 type: 'code', 13054 lang: cap[2], 13055 text: cap[3] || '' 13056 }); 13057 continue; 13058 } 13059 13060 // heading 13061 if (cap = this.rules.heading.exec(src)) { 13062 src = src.substring(cap[0].length); 13063 this.tokens.push({ 13064 type: 'heading', 13065 depth: cap[1].length, 13066 text: cap[2] 13067 }); 13068 continue; 13069 } 13070 13071 // table no leading pipe (gfm) 13072 if (top && (cap = this.rules.nptable.exec(src))) { 13073 src = src.substring(cap[0].length); 13074 13075 item = { 13076 type: 'table', 13077 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), 13078 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), 13079 cells: cap[3].replace(/\n$/, '').split('\n') 13080 }; 13081 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'; 13089 } else { 13090 item.align[i] = null; 13091 } 13092 } 13093 13094 for (i = 0; i < item.cells.length; i++) { 13095 item.cells[i] = item.cells[i].split(/ *\| */); 13096 } 13097 13098 this.tokens.push(item); 13099 13100 continue; 13101 } 13102 13103 // lheading 13104 if (cap = this.rules.lheading.exec(src)) { 13105 src = src.substring(cap[0].length); 13106 this.tokens.push({ 13107 type: 'heading', 13108 depth: cap[2] === '=' ? 1 : 2, 13109 text: cap[1] 13110 }); 13111 continue; 13112 } 13113 13114 // hr 13115 if (cap = this.rules.hr.exec(src)) { 13116 src = src.substring(cap[0].length); 13117 this.tokens.push({ 13118 type: 'hr' 13119 }); 13120 continue; 13121 } 13122 13123 // blockquote 13124 if (cap = this.rules.blockquote.exec(src)) { 13125 src = src.substring(cap[0].length); 13126 13127 this.tokens.push({ 13128 type: 'blockquote_start' 13129 }); 13130 13131 cap = cap[0].replace(/^ *> ?/gm, ''); 13132 13133 // Pass `top` to keep the current 13134 // "toplevel" state. This is exactly 13135 // how markdown.pl works. 13136 this.token(cap, top, true); 13137 13138 this.tokens.push({ 13139 type: 'blockquote_end' 13140 }); 13141 13142 continue; 13143 } 13144 13145 // list 13146 if (cap = this.rules.list.exec(src)) { 13147 src = src.substring(cap[0].length); 13148 bull = cap[2]; 13149 13150 this.tokens.push({ 13151 type: 'list_start', 13152 ordered: bull.length > 1 13153 }); 13154 13155 // Get each top-level item. 13156 cap = cap[0].match(this.rules.item); 13157 13158 next = false; 13159 l = cap.length; 13160 i = 0; 13161 13162 for (; i < l; i++) { 13163 item = cap[i]; 13164 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+\.) +/, ''); 13169 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, ''); 13177 } 13178 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; 13185 i = l - 1; 13186 } 13187 } 13188 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); 13193 if (i !== l - 1) { 13194 next = item.charAt(item.length - 1) === '\n'; 13195 if (!loose) loose = next; 13196 } 13197 13198 this.tokens.push({ 13199 type: loose 13200 ? 'loose_item_start' 13201 : 'list_item_start' 13202 }); 13203 13204 // Recurse. 13205 this.token(item, false, bq); 13206 13207 this.tokens.push({ 13208 type: 'list_item_end' 13209 }); 13210 } 13211 13212 this.tokens.push({ 13213 type: 'list_end' 13214 }); 13215 13216 continue; 13217 } 13218 13219 // html 13220 if (cap = this.rules.html.exec(src)) { 13221 src = src.substring(cap[0].length); 13222 this.tokens.push({ 13223 type: this.options.sanitize 13224 ? 'paragraph' 13225 : 'html', 13226 pre: !this.options.sanitizer 13227 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), 13228 text: cap[0] 13229 }); 13230 continue; 13231 } 13232 13233 // def 13234 if ((!bq && top) && (cap = this.rules.def.exec(src))) { 13235 src = src.substring(cap[0].length); 13236 this.tokens.links[cap[1].toLowerCase()] = { 13237 href: cap[2], 13238 title: cap[3] 13239 }; 13240 continue; 13241 } 13242 13243 // table (gfm) 13244 if (top && (cap = this.rules.table.exec(src))) { 13245 src = src.substring(cap[0].length); 13246 13247 item = { 13248 type: 'table', 13249 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), 13250 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), 13251 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') 13252 }; 13253 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'; 13261 } else { 13262 item.align[i] = null; 13263 } 13264 } 13265 13266 for (i = 0; i < item.cells.length; i++) { 13267 item.cells[i] = item.cells[i] 13268 .replace(/^ *\| *| *\| *$/g, '') 13269 .split(/ *\| */); 13270 } 13271 13272 this.tokens.push(item); 13273 13274 continue; 13275 } 13276 13277 // top-level paragraph 13278 if (top && (cap = this.rules.paragraph.exec(src))) { 13279 src = src.substring(cap[0].length); 13280 this.tokens.push({ 13281 type: 'paragraph', 13282 text: cap[1].charAt(cap[1].length - 1) === '\n' 13283 ? cap[1].slice(0, -1) 13284 : cap[1] 13285 }); 13286 continue; 13287 } 13288 13289 // text 13290 if (cap = this.rules.text.exec(src)) { 13291 // Top-level should never reach here. 13292 src = src.substring(cap[0].length); 13293 this.tokens.push({ 13294 type: 'text', 13295 text: cap[0] 13296 }); 13297 continue; 13298 } 13299 13300 if (src) { 13301 throw new
13302 Error('Infinite loop on byte: ' + src.charCodeAt(0)); 13303 } 13304 } 13305 13306 return this.tokens; 13307}; 13308 13309/** 13310 * Inline-Level Grammar 13311 */ 13312 13313var inline = { 13314 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, 13315 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, 13316 url: noop, 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*$)/, 13325 del: noop, 13326 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/ 13327}; 13328 13329inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/; 13330inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/; 13331 13332inline.link = replace(inline.link) 13333 ('inside', inline._inside) 13334 ('href', inline._href) 13335 (); 13336 13337inline.reflink = replace(inline.reflink) 13338 ('inside', inline._inside) 13339 (); 13340 13341/** 13342 * Normal Inline Grammar 13343 */ 13344 13345inline.normal = merge({}, inline); 13346 13347/** 13348 * Pedantic Inline Grammar 13349 */ 13350 13351inline.pedantic = merge({}, inline.normal, { 13352 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, 13353 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/ 13354}); 13355 13356/** 13357 * GFM Inline Grammar 13358 */ 13359 13360inline.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) 13365 (']|', '~]|') 13366 ('|', '|https?://|') 13367 () 13368}); 13369 13370/** 13371 * GFM + Line Breaks Inline Grammar 13372 */ 13373 13374inline.breaks = merge({}, inline.gfm, { 13375 br: replace(inline.br)('{2,}', '*')(), 13376 text: replace(inline.gfm.text)('{2,}', '*')() 13377}); 13378 13379/** 13380 * Inline Lexer & Compiler 13381 */ 13382 13383function 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; 13389 13390 if (!this.links) { 13391 throw new 13392 Error('Tokens array requires a `links` property.'); 13393 } 13394 13395 if (this.options.gfm) { 13396 if (this.options.breaks) { 13397 this.rules = inline.breaks; 13398 } else { 13399 this.rules = inline.gfm; 13400 } 13401 } else if (this.options.pedantic) { 13402 this.rules = inline.pedantic; 13403 } 13404} 13405 13406/** 13407 * Expose Inline Rules 13408 */ 13409 13410InlineLexer.rules = inline; 13411 13412/** 13413 * Static Lexing/Compiling Method 13414 */ 13415 13416InlineLexer.output = function(src, links, options) { 13417 var inline = new InlineLexer(links, options); 13418 return inline.output(src); 13419}; 13420 13421/** 13422 * Lexing/Compiling 13423 */ 13424 13425InlineLexer.prototype.output = function(src) { 13426 var out = '' 13427 , link 13428 , text 13429 , href 13430 , cap; 13431 13432 while (src) { 13433 // escape 13434 if (cap = this.rules.escape.exec(src)) { 13435 src = src.substring(cap[0].length); 13436 out += cap[1]; 13437 continue; 13438 } 13439 13440 // autolink 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; 13448 } else { 13449 text = escape(cap[1]); 13450 href = text; 13451 } 13452 out += this.renderer.link(href, null, text); 13453 continue; 13454 } 13455 13456 // url (gfm) 13457 if (!this.inLink && (cap = this.rules.url.exec(src))) { 13458 src = src.substring(cap[0].length); 13459 text = escape(cap[1]); 13460 href = text; 13461 out += this.renderer.link(href, null, text); 13462 continue; 13463 } 13464 13465 // tag 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; 13471 } 13472 src = src.substring(cap[0].length); 13473 out += this.options.sanitize 13474 ? this.options.sanitizer 13475 ? this.options.sanitizer(cap[0]) 13476 : escape(cap[0]) 13477 : cap[0] 13478 continue; 13479 } 13480 13481 // link 13482 if (cap = this.rules.link.exec(src)) { 13483 src = src.substring(cap[0].length); 13484 this.inLink = true; 13485 out += this.outputLink(cap, { 13486 href: cap[2], 13487 title: cap[3] 13488 }); 13489 this.inLink = false; 13490 continue; 13491 } 13492 13493 // reflink, nolink 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; 13502 continue; 13503 } 13504 this.inLink = true; 13505 out += this.outputLink(cap, link); 13506 this.inLink = false; 13507 continue; 13508 } 13509 13510 // strong 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])); 13514 continue; 13515 } 13516 13517 // em 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])); 13521 continue; 13522 } 13523 13524 // code 13525 if (cap = this.rules.code.exec(src)) { 13526 src = src.substring(cap[0].length); 13527 out += this.renderer.codespan(escape(cap[2], true)); 13528 continue; 13529 } 13530 13531 // br 13532 if (cap = this.rules.br.exec(src)) { 13533 src = src.substring(cap[0].length); 13534 out += this.renderer.br(); 13535 continue; 13536 } 13537 13538 // del (gfm) 13539 if (cap = this.rules.del.exec(src)) { 13540 src = src.substring(cap[0].length); 13541 out += this.renderer.del(this.output(cap[1])); 13542 continue; 13543 } 13544 13545 // text 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]))); 13549 continue; 13550 } 13551 13552 if (src) { 13553 throw new 13554 Error('Infinite loop on byte: ' + src.charCodeAt(0)); 13555 } 13556 } 13557 13558 return out; 13559}; 13560 13561/** 13562 * Compile Link 13563 */ 13564 13565InlineLexer.prototype.outputLink = function(cap, link) { 13566 var href = escape(link.href) 13567 , title = link.title ? escape(link.title) : null; 13568 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])); 13572}; 13573 13574/** 13575 * Smartypants Transformations 13576 */ 13577 13578InlineLexer.prototype.smartypants = function(text) { 13579 if (!this.options.smartypants) return text; 13580 return text 13581 // em-dashes 13582 .replace(/---/g, '\u2014') 13583 // en-dashes 13584 .replace(/--/g, '\u2013') 13585 // opening singles 13586 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') 13587 // closing singles & apostrophes 13588 .replace(/'/g, '\u2019') 13589 // opening doubles 13590 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') 13591 // closing doubles 13592 .replace(/"/g, '\u201d') 13593 // ellipses 13594 .replace(/\.{3}/g, '\u2026'); 13595}; 13596 13597/** 13598 * Mangle Links 13599 */ 13600 13601InlineLexer.prototype.mangle = function(text) { 13602 if (!this.options.mangle) return text; 13603 var out = '' 13604 , l = text.length 13605 , i = 0 13606 , ch; 13607 13608 for (; i < l; i++) { 13609 ch = text.charCodeAt(i); 13610 if (Math.random() > 0.5) { 13611 ch = 'x' + ch.toString(16); 13612 } 13613 out += '&#' + ch + ';'; 13614 } 13615 13616 return out; 13617}; 13618 13619/** 13620 * Renderer 13621 */ 13622 13623function Renderer(options) { 13624 this.options = options || {}; 13625} 13626 13627Renderer.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) { 13631 escaped = true; 13632 code = out; 13633 } 13634 } 13635 13636 if (!lang) { 13637 return '<pre><code>' 13638 + (escaped ? code : escape(code, true)) 13639 + '\n</code></pre>'; 13640 } 13641 13642 return '<pre><code class="' 13643 + this.options.langPrefix 13644 + escape(lang, true) 13645 + '">' 13646 + (escaped ? code : escape(code, true)) 13647 + '\n</code></pre>\n'; 13648}; 13649 13650Renderer.prototype.blockquote = function(quote) { 13651 return '<blockquote>\n' + quote + '</blockquote>\n'; 13652}; 13653 13654Renderer.prototype.html = function(html) { 13655 return html; 13656}; 13657 13658Renderer.prototype.heading = function(text, level, raw) { 13659 return '<h' 13660 + level 13661 + ' id="' 13662 + this.options.headerPrefix 13663 + raw.toLowerCase().replace(/[^\w]+/g, '-') 13664 + '">' 13665 + text 13666 + '</h' 13667 + level 13668 + '>\n'; 13669}; 13670 13671Renderer.prototype.hr = function() { 13672 return this.options.xhtml ? '<hr/>\n' : '<hr>\n'; 13673}; 13674 13675Renderer.prototype.list = function(body, ordered) { 13676 var type = ordered ? 'ol' : 'ul'; 13677 return '<' + type + '>\n' + body + '</' + type + '>\n'; 13678}; 13679 13680Renderer.prototype.listitem = function(text) { 13681 return '<li>
13681' + text + '</li>\n'; 13682}; 13683 13684Renderer.prototype.paragraph = function(text) { 13685 return '<p>' + text + '</p>\n'; 13686}; 13687 13688Renderer.prototype.table = function(header, body) { 13689 return '<table>\n' 13690 + '<thead>\n' 13691 + header 13692 + '</thead>\n' 13693 + '<tbody>\n' 13694 + body 13695 + '</tbody>\n' 13696 + '</table>\n'; 13697}; 13698 13699Renderer.prototype.tablerow = function(content) { 13700 return '<tr>\n' + content + '</tr>\n'; 13701}; 13702 13703Renderer.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'; 13709}; 13710 13711// span level renderer 13712Renderer.prototype.strong = function(text) { 13713 return '<strong>' + text + '</strong>'; 13714}; 13715 13716Renderer.prototype.em = function(text) { 13717 return '<em>' + text + '</em>'; 13718}; 13719 13720Renderer.prototype.codespan = function(text) { 13721 return '<code>' + text + '</code>'; 13722}; 13723 13724Renderer.prototype.br = function() { 13725 return this.options.xhtml ? '<br/>' : '<br>'; 13726}; 13727 13728Renderer.prototype.del = function(text) { 13729 return '<del>' + text + '</del>'; 13730}; 13731 13732Renderer.prototype.link = function(href, title, text) { 13733 if (this.options.sanitize) { 13734 try { 13735 var prot = decodeURIComponent(unescape(href)) 13736 .replace(/[^\w:]/g, '') 13737 .toLowerCase(); 13738 } catch (e) { 13739 return ''; 13740 } 13741 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) { 13742 return ''; 13743 } 13744 } 13745 var out = '<a href="' + href + '"'; 13746 if (title) { 13747 out += ' title="' + title + '"'; 13748 } 13749 out += '>
13749' + text + '</a>'; 13750 return out; 13751}; 13752 13753Renderer.prototype.image = function(href, title, text) { 13754 var out = '<img src="' + href + '" alt="' + text + '"'; 13755 if (title) { 13756 out += ' title="' + title + '"'; 13757 } 13758 out += this.options.xhtml ? '/>' : '>'; 13759 return out; 13760}; 13761 13762Renderer.prototype.text = function(text) { 13763 return text; 13764}; 13765 13766/** 13767 * Parsing & Compiling 13768 */ 13769 13770function Parser(options) { 13771 this.tokens = []; 13772 this.token = null; 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; 13777} 13778 13779/** 13780 * Static Parse Method 13781 */ 13782 13783Parser.parse = function(src, options, renderer) { 13784 var parser = new Parser(options, renderer); 13785 return parser.parse(src); 13786}; 13787 13788/** 13789 * Parse Loop 13790 */ 13791 13792Parser.prototype.parse = function(src) { 13793 this.inline = new InlineLexer(src.links, this.options, this.renderer); 13794 this.tokens = src.reverse(); 13795 13796 var out = ''; 13797 while (this.next()) { 13798 out += this.tok(); 13799 } 13800 13801 return out; 13802}; 13803 13804/** 13805 * Next Token 13806 */ 13807 13808Parser.prototype.next = function() { 13809 return this.token = this.tokens.pop(); 13810}; 13811 13812/** 13813 * Preview Next Token 13814 */ 13815 13816Parser.prototype.peek = function() { 13817 return this.tokens[this.tokens.length - 1] || 0; 13818}; 13819 13820/** 13821 * Parse Text Tokens 13822 */ 13823 13824Parser.prototype.parseText = function() { 13825 var body = this.token.text; 13826 13827 while (this.peek().type === 'text') { 13828 body += '\n' + this.next().text; 13829 } 13830 13831 return this.inline.output(body); 13832}; 13833 13834/** 13835 * Parse Current Token 13836 */ 13837 13838Parser.prototype.tok = function() { 13839 switch (this.token.type) { 13840 case 'space': { 13841 return ''; 13842 } 13843 case 'hr': { 13844 return this.renderer.hr(); 13845 } 13846 case 'heading': { 13847 return this.renderer.heading( 13848 this.inline.output(this.token.text), 13849 this.token.depth, 13850 this.token.text); 13851 } 13852 case 'code': { 13853 return this.renderer.code(this.token.text, 13854 this.token.lang, 13855 this.token.escaped); 13856 } 13857 case 'table': { 13858 var header = '' 13859 , body = '' 13860 , i 13861 , row 13862 , cell 13863 , flags 13864 , j; 13865 13866 // header 13867 cell = ''; 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] } 13873 ); 13874 } 13875 header += this.renderer.tablerow(cell); 13876 13877 for (i = 0; i < this.token.cells.length; i++) { 13878 row = this.token.cells[i]; 13879 13880 cell = ''; 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] } 13885 ); 13886 } 13887 13888 body += this.renderer.tablerow(cell); 13889 } 13890 return this.renderer.table(header, body); 13891 } 13892 case 'blockquote_start': { 13893 var body = ''; 13894 13895 while (this.next().type !== 'blockquote_end') { 13896 body += this.tok(); 13897 } 13898 13899 return this.renderer.blockquote(body); 13900 } 13901 case 'list_start': { 13902 var body = '' 13903 , ordered = this.token.ordered; 13904 13905 while (this.next().type !== 'list_end') { 13906 body += this.tok(); 13907 } 13908 13909 return this.renderer.list(body, ordered); 13910 } 13911 case 'list_item_start': { 13912 var body = ''; 13913 13914 while (this.next().type !== 'list_item_end') { 13915 body += this.token.type === 'text' 13916 ? this.parseText() 13917 : this.tok(); 13918 } 13919 13920 return this.renderer.listitem(body); 13921 } 13922 case 'loose_item_start': { 13923 var body = ''; 13924 13925 while (this.next().type !== 'list_item_end') { 13926 body += this.tok(); 13927 } 13928 13929 return this.renderer.listitem(body); 13930 } 13931 case 'html': { 13932 var html = !this.token.pre && !this.options.pedantic 13933 ? this.inline.output(this.token.text) 13934 : this.token.text; 13935 return this.renderer.html(html); 13936 } 13937 case 'paragraph': {
13938 return this.renderer.paragraph(this.inline.output(this.token.text)); 13939 } 13940 case 'text': { 13941 return this.renderer.paragraph(this.parseText()); 13942 } 13943 } 13944}; 13945 13946/** 13947 * Helpers 13948 */ 13949 13950function escape(html, encode) { 13951 return html 13952 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') 13953 .replace(/</g, '<') 13954 .replace(/>/g, '>') 13955 .replace(/"/g, '"') 13956 .replace(/'/g, '''); 13957} 13958 13959function 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)); 13967 } 13968 return ''; 13969 }); 13970} 13971 13972function replace(regex, opt) { 13973 regex = regex.source; 13974 opt = opt || ''; 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); 13980 return self; 13981 }; 13982} 13983 13984function noop() {} 13985noop.exec = noop; 13986 13987function merge(obj) { 13988 var i = 1 13989 , target 13990 , key; 13991 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]; 13997 } 13998 } 13999 } 14000 14001 return obj; 14002} 14003 14004 14005/** 14006 * Marked 14007 */ 14008 14009function marked(src, opt, callback) { 14010 if (callback || typeof opt === 'function') { 14011 if (!callback) { 14012 callback = opt; 14013 opt = null; 14014 } 14015 14016 opt = merge({}, marked.defaults, opt || {}); 14017 14018 var highlight = opt.highlight 14019 , tokens 14020 , pending 14021 , i = 0; 14022 14023 try { 14024 tokens = Lexer.lex(src, opt) 14025 } catch (e) { 14026 return callback(e); 14027 } 14028 14029 pending = tokens.length; 14030 14031 var done = function(err) { 14032 if (err) { 14033 opt.highlight = highlight; 14034 return callback(err); 14035 } 14036 14037 var out; 14038 14039 try { 14040 out = Parser.parse(tokens, opt); 14041 } catch (e) { 14042 err = e; 14043 } 14044 14045 opt.highlight = highlight; 14046 14047 return err 14048 ? callback(err) 14049 : callback(null, out); 14050 }; 14051 14052 if (!highlight || highlight.length < 3) { 14053 return done(); 14054 } 14055 14056 delete opt.highlight; 14057 14058 if (!pending) return done(); 14059 14060 for (; i < tokens.length; i++) { 14061 (function(token) { 14062 if (token.type !== 'code') { 14063 return --pending || done(); 14064 } 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(); 14069 } 14070 token.text = code; 14071 token.escaped = true; 14072 --pending || done(); 14073 }); 14074 })(tokens[i]); 14075 } 14076 14077 return; 14078 } 14079 try { 14080 if (opt) opt = merge({}, marked.defaults, opt); 14081 return Parser.parse(Lexer.lex(src, opt), opt); 14082 } catch (e) { 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) 14087 + '</pre>'; 14088 } 14089 throw e; 14090 } 14091} 14092 14093/** 14094 * Options 14095 */ 14096 14097marked.options = 14098marked.setOptions = function(opt) { 14099 merge(marked.defaults, opt); 14100 return marked; 14101}; 14102 14103marked.defaults = { 14104 gfm: true, 14105 tables: true, 14106 breaks: false, 14107 pedantic: false, 14108 sanitize: false, 14109 sanitizer: null, 14110 mangle: true, 14111 smartLists: false, 14112 silent: false, 14113 highlight: null, 14114 langPrefix: 'lang-', 14115 smartypants: false, 14116 headerPrefix: '', 14117 renderer: new Renderer, 14118 xhtml: false 14119}; 14120 14121/** 14122 * Expose 14123 */ 14124 14125marked.Parser = Parser; 14126marked.parser = Parser.parse; 14127 14128marked.Renderer = Renderer; 14129 14130marked.Lexer = Lexer; 14131marked.lexer = Lexer.lex; 14132 14133marked.InlineLexer = InlineLexer; 14134marked.inlineLexer = InlineLexer.output; 14135 14136marked.parse = marked; 14137 14138if (typeof module !== 'undefined' && typeof exports === 'object') { 14139 module.exports = marked; 14140} else if (typeof define === 'function' && define.amd) { 14141 define(function() { return marked; }); 14142} else { 14143 this.marked = marked; 14144} 14145 14146}).call(function() { 14147 return this || (typeof window !== 'undefined' ? window : global); 14148}()); 14149 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){ 14153'use strict'; 14154 14155/** 14156 * Typo is a JavaScript implementation of a spellchecker using hunspell-style 14157 * dictionaries. 14158 */ 14159 14160/** 14161 * Typo constructor. 14162 * 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 14179 * environment. 14180 * {Object} [flags]: flag information. 14181 * 14182 * 14183 * @returns {Typo} A Typo object. 14184 */ 14185 14186var Typo = function (dictionary, affData, wordsData, settings) { 14187 settings = settings || {}; 14188 14189 this.dictionary = null; 14190 14191 this.rules = {}; 14192 this.dictionaryTable = {}; 14193 14194 this.compoundRules = []; 14195 this.compoundRuleCodes = {}; 14196 14197 this.replacementTable = []; 14198 14199 this.flags = settings.flags || {}; 14200 14201 if (dictionary) { 14202 this.dictionary = dictionary; 14203 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")); 14207 } else { 14208 if (settings.dictionaryPath) { 14209 var path = settings.dictionaryPath; 14210 } 14211 else if (typeof __dirname !== 'undefined') { 14212 var path = __dirname + '/dictionaries'; 14213 } 14214 else { 14215 var path = './dictionaries'; 14216 } 14217 14218 if (!affData) affData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".aff"); 14219 if (!wordsData) wordsData = this._readFile(path + "/" + dictionary + "/" + dictionary + ".dic"); 14220 } 14221 14222 this.rules = this._parseAFF(affData); 14223 14224 // Save the rule codes that are used in compound rules. 14225 this.compoundRuleCodes = {}; 14226 14227 for (var i = 0, _len = this.compoundRules.length; i < _len; i++) { 14228 var rule = this.compoundRules[i]; 14229 14230 for (var j = 0, _jlen = rule.length; j < _jlen; j++) { 14231 this.compoundRuleCodes[rule[j]] = []; 14232 } 14233 } 14234 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] = []; 14239 } 14240 14241 this.dictionaryTable = this._parseDIC(wordsData); 14242 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]; 14248 } 14249 } 14250 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]; 14256 14257 var expressionText = ""; 14258 14259 for (var j = 0, _jlen = ruleText.length; j < _jlen; j++) { 14260 var character = ruleText[j]; 14261 14262 if (character in this.compoundRuleCodes) { 14263 expressionText += "(" + this.compoundRuleCodes[character].join("|") + ")"; 14264 } 14265 else { 14266 expressionText += character; 14267 } 14268 } 14269 14270 this.compoundRules[i] = new RegExp(expressionText, "i"); 14271 } 14272 } 14273 14274 return this; 14275}; 14276 14277Typo.prototype = { 14278 /** 14279 * Loads a Typo instance from a hash of all of the Typo properties. 14280 * 14281 * @param object obj A hash of Typo properties, probably gotten from a JSON.parse(JSON.stringify(typo_instance)). 14282 */ 14283 14284 load : function (obj) { 14285 for (var i in obj) { 14286 this[i] = obj[i]; 14287 } 14288 14289 return this; 14290 }, 14291 14292 /** 14293 * Read the contents of a file. 14294 * 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. 14298 */ 14299 14300 _readFile : function (path, charset) { 14301 if (!charset) charset = "utf8"; 14302 14303 if (typeof XMLHttpRequest !== 'undefined') { 14304 var req = new XMLHttpRequest(); 14305 req.open("GET", path, false); 14306 14307 if (req.overrideMimeType) 14308 req.overrideMimeType("text/plain; charset=" + charset); 14309 14310 req.send(null); 14311 14312 return req.responseText; 14313 } 14314 else if (typeof require !== 'undefined') { 14315 // Node.js 14316 var fs = require("fs"); 14317 14318 try { 14319 if (fs.existsSync(path)) { 14320 var stats = fs.statSync(path);
14321 14322 var fileDescriptor = fs.openSync(path, 'r'); 14323 14324 var buffer = new Buffer(stats.size); 14325 14326 fs.readSync(fileDescriptor, buffer, 0, buffer.length, null); 14327 14328 return buffer.toString(charset, 0, buffer.length); 14329 } 14330 else { 14331 console.log("Path " + path + " does not exist."); 14332 } 14333 } catch (e) { 14334 console.log(e); 14335 return ''; 14336 } 14337 } 14338 }, 14339 14340 /** 14341 * Parse the rules out from a .aff file. 14342 * 14343 * @param {String} data The contents of the affix file. 14344 * @returns object The rules from the file. 14345 */ 14346 14347 _parseAFF : function (data) { 14348 var rules = {}; 14349 14350 // Remove comment lines 14351 data = this._removeAffixComments(data); 14352 14353 var lines = data.split("\n"); 14354 14355 for (var i = 0, _len = lines.length; i < _len; i++) { 14356 var line = lines[i]; 14357 14358 var definitionParts = line.split(/\s+/); 14359 14360 var ruleType = definitionParts[0]; 14361 14362 if (ruleType == "PFX" || ruleType == "SFX") { 14363 var ruleCode = definitionParts[1]; 14364 var combineable = definitionParts[2]; 14365 var numEntries = parseInt(definitionParts[3], 10); 14366 14367 var entries = []; 14368 14369 for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) { 14370 var line = lines[j]; 14371 14372 var lineParts = line.split(/\s+/); 14373 var charactersToRemove = lineParts[2]; 14374 14375 var additionParts = lineParts[3].split("/"); 14376 14377 var charactersToAdd = additionParts[0]; 14378 if (charactersToAdd === "0") charactersToAdd = ""; 14379 14380 var continuationClasses = this.parseRuleCodes(additionParts[1]); 14381 14382 var regexToMatch = lineParts[4]; 14383 14384 var entry = {}; 14385 entry.add = charactersToAdd; 14386 14387 if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses; 14388 14389 if (regexToMatch !== ".") { 14390 if (ruleType === "SFX") { 14391 entry.match = new RegExp(regexToMatch + "$"); 14392 } 14393 else { 14394 entry.match = new RegExp("^" + regexToMatch); 14395 } 14396 } 14397 14398 if (charactersToRemove != "0") { 14399 if (ruleType === "SFX") { 14400 entry.remove = new RegExp(charactersToRemove + "$"); 14401 } 14402 else { 14403 entry.remove = charactersToRemove; 14404 } 14405 } 14406 14407 entries.push(entry); 14408 } 14409 14410 rules[ruleCode] = { "type" : ruleType, "combineable" : (combineable == "Y"), "entries" : entries }; 14411 14412 i += numEntries; 14413 } 14414 else if (ruleType === "COMPOUNDRULE") { 14415 var numEntries = parseInt(definitionParts[1], 10); 14416 14417 for (var j = i + 1, _jlen = i + 1 + numEntries; j < _jlen; j++) { 14418 var line = lines[j]; 14419 14420 var lineParts = line.split(/\s+/); 14421 this.compoundRules.push(lineParts[1]); 14422 } 14423 14424 i += numEntries; 14425 } 14426 else if (ruleType === "REP") { 14427 var lineParts = line.split(/\s+/); 14428 14429 if (lineParts.length === 3) { 14430 this.replacementTable.push([ lineParts[1], lineParts[2] ]); 14431 } 14432 } 14433 else { 14434 // ONLYINCOMPOUND 14435 // COMPOUNDMIN 14436 // FLAG 14437 // KEEPCASE 14438 // NEEDAFFIX 14439 14440 this.flags[ruleType] = definitionParts[1]; 14441 } 14442 } 14443 14444 return rules; 14445 }, 14446 14447 /** 14448 * Removes comment lines and then cleans up blank lines and trailing whitespace. 14449 * 14450 * @param {String} data The data from an affix file. 14451 * @return {String} The cleaned-up data. 14452 */ 14453 14454 _removeAffixComments : function (data) { 14455 // Remove comments 14456 data = data.replace(/#.*$/mg, ""); 14457 14458 // Trim each line 14459 data = data.replace(/^\s\s*/m, '').replace(/\s\s*$/m, ''); 14460 14461 // Remove blank lines. 14462 data = data.replace(/\n{2,}/g, "\n"); 14463 14464 // Trim the entire string 14465 data = data.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 14466 14467 return data; 14468 }, 14469 14470 /** 14471 * Parses the words out from the .dic file. 14472 * 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. 14476 */ 14477 14478 _parseDIC : function (data) { 14479 data = this._removeDicComments(data); 14480 14481 var lines = data.split("\n"); 14482 var dictionaryTable = {}; 14483 14484 function addWord(word, rules) { 14485 // Some dictionaries will list the same word multiple times with different rule set
vendor: 5,983 bytes, lines 14485-14708
14485s. 14486 if (!(word in dictionaryTable) || typeof dictionaryTable[word] != 'object') { 14487 dictionaryTable[word] = []; 14488 } 14489 14490 dictionaryTable[word].push(rules); 14491 } 14492 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]; 14496 14497 var parts = line.split("/", 2); 14498 14499 var word = parts[0]; 14500 14501 // Now for each affix rule, generate that form of the word. 14502 if (parts.length > 1) { 14503 var ruleCodesArray = this.parseRuleCodes(parts[1]); 14504 14505 // Save the ruleCodes for compound word situations. 14506 if (!("NEEDAFFIX" in this.flags) || ruleCodesArray.indexOf(this.flags.NEEDAFFIX) == -1) { 14507 addWord(word, ruleCodesArray); 14508 } 14509 14510 for (var j = 0, _jlen = ruleCodesArray.length; j < _jlen; j++) { 14511 var code = ruleCodesArray[j]; 14512 14513 var rule = this.rules[code]; 14514 14515 if (rule) { 14516 var newWords = this._applyRule(word, rule); 14517 14518 for (var ii = 0, _iilen = newWords.length; ii < _iilen; ii++) { 14519 var newWord = newWords[ii]; 14520 14521 addWord(newWord, []); 14522 14523 if (rule.combineable) { 14524 for (var k = j + 1; k < _jlen; k++) { 14525 var combineCode = ruleCodesArray[k]; 14526 14527 var combineRule = this.rules[combineCode]; 14528 14529 if (combineRule) { 14530 if (combineRule.combineable && (rule.type != combineRule.type)) { 14531 var otherNewWords = this._applyRule(newWord, combineRule); 14532 14533 for (var iii = 0, _iiilen = otherNewWords.length; iii < _iiilen; iii++) { 14534 var otherNewWord = otherNewWords[iii]; 14535 addWord(otherNewWord, []); 14536 } 14537 } 14538 } 14539 } 14540 } 14541 } 14542 } 14543 14544 if (code in this.compoundRuleCodes) { 14545 this.compoundRuleCodes[code].push(word); 14546 } 14547 } 14548 } 14549 else { 14550 addWord(word.trim(), []); 14551 } 14552 } 14553 14554 return dictionaryTable; 14555 }, 14556 14557 14558 /** 14559 * Removes comment lines and then cleans up blank lines and trailing whitespace. 14560 * 14561 * @param {String} data The data from a .dic file. 14562 * @return {String} The cleaned-up data. 14563 */ 14564 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. 14568 14569 // Remove comments 14570 data = data.replace(/^\t.*$/mg, ""); 14571 14572 return data; 14573 }, 14574 14575 parseRuleCodes : function (textCodes) { 14576 if (!textCodes) { 14577 return []; 14578 } 14579 else if (!("FLAG" in this.flags)) { 14580 return textCodes.split(""); 14581 } 14582 else if (this.flags.FLAG === "long") { 14583 var flags = []; 14584 14585 for (var i = 0, _len = textCodes.length; i < _len; i += 2) { 14586 flags.push(textCodes.substr(i, 2)); 14587 } 14588 14589 return flags; 14590 } 14591 else if (this.flags.FLAG === "num") { 14592 return textCode.split(","); 14593 } 14594 }, 14595 14596 /** 14597 * Applies an affix rule to a word. 14598 * 14599 * @param {String} word The base word. 14600 * @param {Object} rule The affix rule. 14601 * @returns {String[]} The new words generated by the rule. 14602 */ 14603 14604 _applyRule : function (word, rule) { 14605 var entries = rule.entries; 14606 var newWords = []; 14607 14608 for (var i = 0, _len = entries.length; i < _len; i++) { 14609 var entry = entries[i]; 14610 14611 if (!entry.match || word.match(entry.match)) { 14612 var newWord = word; 14613 14614 if (entry.remove) { 14615 newWord = newWord.replace(entry.remove, ""); 14616 } 14617 14618 if (rule.type === "SFX") { 14619 newWord = newWord + entry.add; 14620 } 14621 else { 14622 newWord = entry.add + newWord; 14623 } 14624 14625 newWords.push(newWord); 14626 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]]; 14630 14631 if (continuationRule) { 14632 newWords = newWords.concat(this._applyRule(newWord, continuationRule)); 14633 } 14634 /* 14635 else { 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 14638 // of upper-case. 14639 } 14640 */ 14641 } 14642 } 14643 } 14644 } 14645 14646 return newWords; 14647 }, 14648 14649 /** 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() 14653 * 14654 * @see http://blog.stevenlevithan.com/archives/faster-trim-javascript re:trimming function 14655 * 14656 * @param {String} aWord The word to check. 14657 * @returns {Boolean} 14658 */ 14659 14660 check : function (aWord) { 14661 // Remove leading and trailing whitespace 14662 var trimmedWord = aWord.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 14663 14664 if (this.checkExact(trimmedWord)) { 14665 return true; 14666 } 14667 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(); 14673 14674 if (this.hasFlag(capitalizedWord, "KEEPCASE")) { 14675 // Capitalization variants are not allowed for this word. 14676 return false; 14677 } 14678 14679 if (this.checkExact(capitalizedWord)) { 14680 return true; 14681 } 14682 } 14683 14684 var lowercaseWord = trimmedWord.toLowerCase(); 14685 14686 if (lowercaseWord !== trimmedWord) { 14687 if (this.hasFlag(lowercaseWord, "KEEPCASE")) { 14688 // Capitalization variants are not allowed for this word. 14689 return false; 14690 } 14691 14692 // Check for a lowercase form 14693 if (this.checkExact(lowercaseWord)) { 14694 return true; 14695 } 14696 } 14697 14698 return false; 14699 }, 14700 14701 /** 14702 * Checks whether a word exists in the current dictionary. 14703 * 14704 * @param {String} word The word to check. 14705 * @returns {Boolean} 14706 */ 14707 14708 checkExact : function (word) {
14709 var ruleCodes = this.dictionaryTable[word]; 14710 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])) { 14716 return true; 14717 } 14718 } 14719 } 14720 14721 return false; 14722 } 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])) { 14726 return true; 14727 } 14728 } 14729 14730 return false; 14731 } 14732 }, 14733 14734 /** 14735 * Looks up whether a given word is flagged with a given flag. 14736 * 14737 * @param {String} word The word in question. 14738 * @param {String} flag The flag in question. 14739 * @return {Boolean} 14740 */ 14741 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]); 14746 } 14747 14748 if (wordFlags && wordFlags.indexOf(this.flags[flag]) !== -1) { 14749 return true; 14750 } 14751 } 14752 14753 return false; 14754 }, 14755 14756 /** 14757 * Returns a list of suggestions for a misspelled word. 14758 * 14759 * @see http://www.norvig.com/spell-correct.html for the basis of this suggestor. 14760 * This suggestor is primitive, but it works. 14761 * 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. 14765 */ 14766 14767 alphabet : "", 14768 14769 suggest : function (word, limit) { 14770 if (!limit) limit = 5; 14771 14772 if (this.check(word)) return []; 14773 14774 // Check the replacement table. 14775 for (var i = 0, _len = this.replacementTable.length; i < _len; i++) { 14776 var replacementEntry = this.replacementTable[i]; 14777 14778 if (word.indexOf(replacementEntry[0]) !== -1) { 14779 var correctedWord = word.replace(replacementEntry[0], replacementEntry[1]); 14780 14781 if (this.check(correctedWord)) { 14782 return [ correctedWord ]; 14783 } 14784 } 14785 } 14786 14787 var self = this; 14788 self.alphabet = "abcdefghijklmnopqrstuvwxyz"; 14789 14790 /* 14791 if (!self.alphabet) { 14792 // Use the alphabet as implicitly defined by the words in the dictionary. 14793 var alphaHash = {}; 14794 14795 for (var i in self.dictionaryTable) { 14796 for (var j = 0, _len = i.length; j < _len; j++) { 14797 alphaHash[i[j]] = true; 14798 } 14799 } 14800 14801 for (var i in alphaHash) { 14802 self.alphabet += i; 14803 } 14804 14805 var alphaArray = self.alphabet.split(""); 14806 alphaArray.sort(); 14807 self.alphabet = alphaArray.join(""); 14808 } 14809 */ 14810 14811 function edits1(words) { 14812 var rv = []; 14813 14814 for (var ii = 0, _iilen = words.length; ii < _iilen; ii++) { 14815 var word = words[ii]; 14816 14817 var splits = []; 14818 14819 for (var i = 0, _len = word.length + 1; i < _len; i++) { 14820 splits.push([ word.substring(0, i), word.substring(i, word.length) ]); 14821 } 14822 14823 var deletes = []; 14824 14825 for (var i = 0, _len = splits.length; i < _len; i++) { 14826 var s = splits[i]; 14827 14828 if (s[1]) { 14829 deletes.push(s[0] + s[1].substring(1)); 14830 } 14831 } 14832 14833 var transposes = []; 14834 14835 for (var i = 0, _len = splits.length; i < _len; i++) { 14836 var s = splits[i]; 14837 14838 if (s[1].length > 1) { 14839 transposes.push(s[0] + s[1][1] + s[1][0] + s[1].substring(2)); 14840 } 14841 } 14842 14843 var replaces = []; 14844 14845 for (var i = 0, _len = splits.length; i < _len; i++) { 14846 var s = splits[i]; 14847 14848 if (s[1]) { 14849 for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { 14850 replaces.push(s[0] + self.alphabet[j] + s[1].substring(1)); 14851 } 14852 } 14853 } 14854 14855 var inserts = []; 14856 14857 for (var i = 0, _len = splits.length; i < _len; i++) { 14858 var s = splits[i]; 14859 14860 if (s[1]) { 14861 for (var j = 0, _jlen = self.alphabet.length; j < _jlen; j++) { 14862 replaces.push(s[0] + self.alphabet[j] + s[1]); 14863 } 14864 } 14865 } 14866 14867 rv = rv.concat(deletes); 14868 rv = rv.concat(transposes); 14869 rv = rv.concat(replaces); 14870 rv = rv.concat(inserts); 14871 } 14872 14873 return rv; 14874 } 14875 14876 function known(words) { 14877 var rv = []; 14878 14879 for (var i = 0; i < words.length; i++) { 14880 if (self.check(words[i])) { 14881 rv.push(words[i]); 14882 } 14883 } 14884 14885 return rv; 14886 } 14887 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); 14892 14893 var corrections = known(ed1).concat(known(ed2)); 14894 14895 // Sort the edits based on how many different ways they were created. 14896 var weighted_corrections = {}; 14897 14898 for (var i = 0, _len = corrections.length; i < _len; i++) { 14899 if (!(corrections[i] in weighted_corrections)) { 14900 weighted_corrections[corrections[i]] = 1; 14901 } 14902 else { 14903 weighted_corrections[corrections[i]] += 1; 14904 } 14905 } 14906 14907 var sorted_corrections = []; 14908 14909 for (var i in weighted_corrections) { 14910 sorted_corrections.push([ i, weighted_corrections[i] ]); 14911 } 14912 14913 function sorter(a, b) { 14914 if (a[1] < b[1]) { 14915 return -1; 14916 } 14917 14918 return 1; 14919 } 14920 14921 sorted_corrections.sort(sorter).reverse(); 14922 14923 var rv = []; 14924 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]); 14928 } 14929 } 14930 14931 return rv; 14932 } 14933 14934 return correct(word); 14935 } 14936}; 14937 14938// Support for use as a node.js module. 14939if (typeof module !== 'undefined') { 14940 module.exports = Typo; 14941} 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 14946 14947var CodeMirror = require("codemirror"); 14948 14949CodeMirror.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; 14954 14955 if (inList) { 14956 cm.execCommand("indentMore"); 14957 return; 14958 } 14959 14960 if (cm.options.indentWithTabs) { 14961 cm.execCommand("insertTab"); 14962 } 14963 else { 14964 var spaces = Array(cm.options.tabSize + 1).join(" "); 14965 cm.replaceSelection(spaces); 14966 } 14967}; 14968 14969CodeMirror.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; 14974 14975 if (inList) { 14976 cm.execCommand("indentLess"); 14977 return; 14978 } 14979 14980 if (cm.options.indentWithTabs) { 14981 cm.execCommand("insertTab"); 14982 } 14983 else { 14984 var spaces = Array(cm.options.tabSize + 1).join(" "); 14985 cm.replaceSelection(spaces); 14986 } 14987}; 14988 14989},{"codemirror":10}],20:[function(require,module,exports){ 14990/*global require,module*/ 14991"use strict";
14992var CodeMirror = require("codemirror"); 14993require("codemirror/addon/edit/continuelist.js"); 14994require("./codemirror/tablist"); 14995require("codemirror/addon/display/fullscreen.js"); 14996require("codemirror/mode/markdown/markdown.js"); 14997require("codemirror/addon/mode/overlay.js"); 14998require("codemirror/addon/display/placeholder.js"); 14999require("codemirror/addon/selection/mark-selection.js"); 15000require("codemirror/mode/gfm/gfm.js"); 15001require("codemirror/mode/xml/xml.js"); 15002var CodeMirrorSpellChecker = require("codemirror-spell-checker"); 15003var marked = require("marked"); 15004 15005 15006// Some variables 15007var isMac = /Mac/.test(navigator.platform); 15008 15009// Mapping of actions that can be bound to keyboard shortcuts or toolbar buttons 15010var bindings = { 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, 15029 "undo": undo, 15030 "redo": redo, 15031 "toggleSideBySide": toggleSideBySide, 15032 "toggleFullScreen": toggleFullScreen 15033}; 15034 15035var shortcuts = { 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" 15050}; 15051 15052var getBindingName = function(f) { 15053 for(var key in bindings) { 15054 if(bindings[key] === f) { 15055 return key; 15056 } 15057 } 15058 return null; 15059}; 15060 15061var isMobile = function() { 15062 var check = false; 15063 (function(a) { 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
15064= true; 15065 })(navigator.userAgent || navigator.vendor || window.opera); 15066 return check; 15067}; 15068 15069 15070/** 15071 * Fix shortcut. Mac use Command, others use Ctrl. 15072 */ 15073function fixShortcut(name) { 15074 if(isMac) { 15075 name = name.replace("Ctrl", "Cmd"); 15076 } else { 15077 name = name.replace("Cmd", "Ctrl"); 15078 } 15079 return name; 15080} 15081 15082 15083/** 15084 * Create icon element for toolbar. 15085 */ 15086function createIcon(options, enableTooltips, shortcuts) { 15087 options = options || {}; 15088 var el = document.createElement("a"); 15089 enableTooltips = (enableTooltips == undefined) ? true : enableTooltips; 15090 15091 if(options.title && enableTooltips) { 15092 el.title = createTootlip(options.title, options.action, shortcuts); 15093 15094 if(isMac) { 15095 el.title = el.title.replace("Ctrl", "â"); 15096 el.title = el.title.replace("Alt", "â¥"); 15097 } 15098 } 15099 15100 el.tabIndex = -1; 15101 el.className = options.className; 15102 return el; 15103} 15104 15105function createSep() { 15106 var el = document.createElement("i"); 15107 el.className = "separator"; 15108 el.innerHTML = "|"; 15109 return el; 15110} 15111 15112function createTootlip(title, action, shortcuts) { 15113 var actionName; 15114 var tooltip = title; 15115 15116 if(action) { 15117 actionName = getBindingName(action); 15118 if(shortcuts[actionName]) { 15119 tooltip += " (" + fixShortcut(shortcuts[actionName]) + ")"; 15120 } 15121 } 15122 15123 return tooltip; 15124} 15125 15126/** 15127 * The state of CodeMirror at the given position. 15128 */ 15129function getState(cm, pos) { 15130 pos = pos || cm.getCursor("start"); 15131 var stat = cm.getTokenAt(pos); 15132 if(!stat.type) return {}; 15133 15134 var types = stat.type.split(" "); 15135 15136 var ret = {}, 15137 data, text; 15138 for(var i = 0; i < types.length; i++) { 15139 data = types[i]; 15140 if(data === "strong") { 15141 ret.bold = true; 15142 } else if(data === "variable-2") { 15143 text = cm.getLine(pos.line); 15144 if(/^\s*\d+\.\s/.test(text)) { 15145 ret["ordered-list"] = true; 15146 } else { 15147 ret["unordered-list"] = true; 15148 } 15149 } else if(data === "atom") { 15150 ret.quote = true; 15151 } else if(data === "em") { 15152 ret.italic = true; 15153 } else if(data === "quote") { 15154 ret.quote = true; 15155 } else if(data === "strikethrough") { 15156 ret.strikethrough = true; 15157 } else if(data === "comment") { 15158 ret.code = true; 15159 } else if(data === "link") { 15160 ret.link = true; 15161 } else if(data === "tag") { 15162 ret.image = true; 15163 } else if(data.match(/^header(\-[1-6])?$/)) { 15164 ret[data.replace("header", "heading")] = true; 15165 } 15166 } 15167 return ret; 15168} 15169 15170 15171// Saved overflow setting 15172var saved_overflow = ""; 15173 15174/** 15175 * Toggle full screen of the editor. 15176 */ 15177function toggleFullScreen(editor) { 15178 // Set fullscreen 15179 var cm = editor.codemirror; 15180 cm.setOption("fullScreen", !cm.getOption("fullScreen")); 15181 15182 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"; 15187 } else { 15188 document.body.style.overflow = saved_overflow; 15189 } 15190 15191 15192 // Update toolbar class 15193 var wrap = cm.getWrapperElement(); 15194 15195 if(!/fullscreen/.test(wrap.previousSibling.className)) { 15196 wrap.previousSibling.className += " fullscreen"; 15197 } else { 15198 wrap.previousSibling.className = wrap.previousSibling.className.replace(/\s*fullscreen\b/, ""); 15199 } 15200 15201 15202 // Update toolbar button 15203 var toolbarButton = editor.toolbarElements.fullscreen;
15204 15205 if(!/active/.test(toolbarButton.className)) { 15206 toolbarButton.className += " active"; 15207 } else { 15208 toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, ""); 15209 } 15210 15211 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); 15216} 15217 15218 15219/** 15220 * Action for toggling bold. 15221 */ 15222function toggleBold(editor) { 15223 _toggleBlock(editor, "bold", editor.options.blockStyles.bold); 15224} 15225 15226 15227/** 15228 * Action for toggling italic. 15229 */ 15230function toggleItalic(editor) { 15231 _toggleBlock(editor, "italic", editor.options.blockStyles.italic); 15232} 15233 15234 15235/** 15236 * Action for toggling strikethrough. 15237 */ 15238function toggleStrikethrough(editor) { 15239 _toggleBlock(editor, "strikethrough", "~~"); 15240} 15241 15242/** 15243 * Action for toggling code block. 15244 */ 15245function toggleCodeBlock(editor) { 15246 var fenceCharsToInsert = editor.options.blockStyles.code; 15247 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; 15252 } 15253 return line.styles && line.styles[2] && line.styles[2].indexOf("formatting-code-block") !== -1; 15254 } 15255 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; 15259 } 15260 15261 function code_type(cm, line_num, line, firstTok, lastTok) { 15262 /* 15263 * Return "single", "indented", "fenced" or false 15264 * 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. 15267 */ 15268 line = line || cm.getLineHandle(line_num); 15269 firstTok = firstTok || cm.getTokenAt({ 15270 line: line_num, 15271 ch: 1 15272 }); 15273 lastTok = lastTok || (!!line.text && cm.getTokenAt({ 15274 line: line_num, 15275 ch: line.text.length - 1 15276 })); 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 15280 return "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 15283 return false; 15284 } else if(token_state(firstTok).fencedChars || token_state(lastTok).fencedChars || fencing_line(line)) { 15285 return "fenced"; 15286 } else { 15287 return "single"; 15288 } 15289 } 15290 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; 15297 if(sel_multi) { 15298 end_line_sel++; 15299 } 15300 // handle last char including \n or not 15301 if(sel_multi && cur_end.ch === 0) { 15302 repl_end = fenceCharsToInsert + "\n"; 15303 end_line_sel--; 15304 } 15305 _replaceSelection(cm, false, [repl_start, repl_end]); 15306 cm.setSelection({ 15307 line: start_line_sel, 15308 ch: 0 15309 }, { 15310 line: end_line_sel, 15311 ch: 0 15312 }); 15313 } 15314 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; 15325 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, 15332 ch: 0 15333 }, { 15334 line: cur_start.line, 15335 ch: 99999999999999 15336 }); 15337 cur_start.ch--; 15338 if(cur_start !== cur_end) { 15339 cur_end.ch--; 15340 } 15341 cm.setSelection(cur_start, cur_end); 15342 cm.focus(); 15343 } else if(is_code === "fenced") { 15344 if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) { 15345 // use selection 15346
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)) { 15351 break; 15352 } 15353 } 15354 var fencedTok = cm.getTokenAt({ 15355 line: block_start, 15356 ch: 1 15357 }); 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))) { 15363 start_text = ""; 15364 start_line = cur_start.line; 15365 } else if(fencing_line(cm.getLineHandle(cur_start.line - 1))) { 15366 start_text = ""; 15367 start_line = cur_start.line - 1; 15368 } else { 15369 start_text = fence_chars + "\n"; 15370 start_line = cur_start.line; 15371 } 15372 if(fencing_line(cm.getLineHandle(cur_end.line))) { 15373 end_text = ""; 15374 end_line = cur_end.line; 15375 if(cur_end.ch === 0) { 15376 end_line += 1; 15377 } 15378 } else if(cur_end.ch !== 0 && fencing_line(cm.getLineHandle(cur_end.line + 1))) { 15379 end_text = ""; 15380 end_line = cur_end.line + 1; 15381 } else { 15382 end_text = fence_chars + "\n"; 15383 end_line = cur_end.line + 1; 15384 } 15385 if(cur_end.ch === 0) { 15386 // full last line selected, putting cursor at beginning of next 15387 end_line -= 1; 15388 } 15389 cm.operation(function() { 15390 // end line first, so that line numbers don't change 15391 cm.replaceRange(end_text, { 15392 line: end_line, 15393 ch: 0 15394 }, { 15395 line: end_line + (end_text ? 0 : 1), 15396 ch: 0 15397 }); 15398 cm.replaceRange(start_text, { 15399 line: start_line, 15400 ch: 0 15401 }, { 15402 line: start_line + (start_text ? 0 : 1), 15403 ch: 0 15404 }); 15405 }); 15406 cm.setSelection({ 15407 line: start_line + (start_text ? 1 : 0), 15408 ch: 0 15409 }, { 15410 line: end_line + (start_text ? 1 : -1), 15411 ch: 0 15412 }); 15413 cm.focus(); 15414 } else { 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" 15421 } else { 15422 block_end = cur_start.line; 15423 search_from = cur_start.line - 1; // for searching for "start" 15424 } 15425 } 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)) { 15430 break; 15431 } 15432 } 15433 } 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)) { 15439 break; 15440 } 15441 } 15442 } 15443 cm.operation(function() { 15444 cm.replaceRange("", { 15445 line: block_start, 15446 ch: 0 15447 }, { 15448 line: block_start + 1, 15449 ch: 0 15450 }); 15451 cm.replaceRange("", { 15452 line: block_end - 1, 15453 ch: 0 15454 }, { 15455 line: block_end, 15456 ch: 0 15457 }); 15458 }); 15459 cm.focus(); 15460 } 15461 } else if(is_code === "indented") { 15462 if(cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) { 15463 // use selection 15464 block_start = cur_start.line; 15465 block_end = cur_end.line; 15466 if(cur_end.ch === 0) { 15467 block_end--; 15468 } 15469 } else { 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 15475 continue; 15476 } else { 15477 if(code_type(cm, block_start, line) !== "indented") { 15478 block_start += 1; 15479 break; 15480 } 15481 } 15482 } 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 15488 continue; 15489 } else { 15490 if(code_type(cm, block_end, line) !== "indented") { 15491 block_end -= 1; 15492 break; 15493 } 15494 } 15495 } 15496 } 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 15503 }), 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, 15508 ch: 0 15509 }); 15510 } 15511 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 und
15513one :( 15514 } 15515 cm.focus(); 15516 } else { 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); 15522 } else { 15523 _replaceSelection(cm, false, ["`", "`"]); 15524 } 15525 } 15526} 15527 15528/** 15529 * Action for toggling blockquote. 15530 */ 15531function toggleBlockquote(editor) { 15532 var cm = editor.codemirror; 15533 _toggleLine(cm, "quote"); 15534} 15535 15536/** 15537 * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal 15538 */ 15539function toggleHeadingSmaller(editor) { 15540 var cm = editor.codemirror; 15541 _toggleHeading(cm, "smaller"); 15542} 15543 15544/** 15545 * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal 15546 */ 15547function toggleHeadingBigger(editor) { 15548 var cm = editor.codemirror; 15549 _toggleHeading(cm, "bigger"); 15550} 15551 15552/** 15553 * Action for toggling heading size 1 15554 */ 15555function toggleHeading1(editor) { 15556 var cm = editor.codemirror; 15557 _toggleHeading(cm, undefined, 1); 15558} 15559 15560/** 15561 * Action for toggling heading size 2 15562 */ 15563function toggleHeading2(editor) { 15564 var cm = editor.codemirror; 15565 _toggleHeading(cm, undefined, 2); 15566} 15567 15568/** 15569 * Action for toggling heading size 3 15570 */ 15571function toggleHeading3(editor) { 15572 var cm = editor.codemirror; 15573 _toggleHeading(cm, undefined, 3); 15574} 15575 15576 15577/** 15578 * Action for toggling ul. 15579 */ 15580function toggleUnorderedList(editor) { 15581 var cm = editor.codemirror; 15582 _toggleLine(cm, "unordered-list"); 15583} 15584 15585 15586/** 15587 * Action for toggling ol. 15588 */ 15589function toggleOrderedList(editor) { 15590 var cm = editor.codemirror; 15591 _toggleLine(cm, "ordered-list"); 15592} 15593 15594/** 15595 * Action for clean block (remove headline, list, blockquote code, markers) 15596 */ 15597function cleanBlock(editor) { 15598 var cm = editor.codemirror; 15599 _cleanBlock(cm); 15600} 15601 15602/** 15603 * Action for drawing a link. 15604 */ 15605function 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); 15612 if(!url) { 15613 return false; 15614 } 15615 } 15616 _replaceSelection(cm, stat.link, options.insertTexts.link, url); 15617} 15618 15619/** 15620 * Action for drawing an img. 15621 */ 15622function 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); 15629 if(!url) { 15630 return false; 15631 } 15632 } 15633 _replaceSelection(cm, stat.image, options.insertTexts.image, url); 15634} 15635 15636/** 15637 * Action for drawing a table. 15638 */ 15639function 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); 15644} 15645 15646/** 15647 * Action for drawing a horizontal rule. 15648 */ 15649function 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); 15654} 15655 15656 15657/** 15658 * Undo action. 15659 */ 15660function undo(editor) { 15661 var cm = editor.codemirror; 15662 cm.undo(); 15663 cm.focus(); 15664} 15665 15666 15667/** 15668 * Redo action. 15669 */ 15670function redo(editor) { 15671 var cm = editor.codemirror; 15672 cm.redo(); 15673 cm.focus(); 15674} 15675 15676 15677/** 15678 * Toggle side by side preview 15679 */ 15680function 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, "" 15689 ); 15690 toolbarButton.className = toolbarButton.className.replace(/\s*active\s*/g, ""); 15691 wrapper.className = wrapper.className.replace(/\s*CodeMirror-sided\s*/g, " "); 15692 } else { 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"; 15700 }, 1); 15701 toolbarButton.className += " active"; 15702 wrapper.className += " CodeMirror-sided"; 15703 useSideBySideListener = true; 15704 } 15705 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, "" 15711 ); 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, ""); 15716 } 15717 15718 var sideBySideRenderingFunction = function() { 15719 preview.innerHTML = editor.options.previewRender(editor.value(), preview); 15720 }; 15721 15722 if(!cm.sideBySideRenderingFunction) { 15723 cm.sideBySideRenderingFunction = sideBySideRenderingFunction; 15724 } 15725 15726 if(useSideBySideListener) { 15727 preview.innerHTML = editor.options.previewRender(editor.value(), preview); 15728 cm.on("update", cm.sideBySideRenderingFunction); 15729 } else { 15730 cm.off("update", cm.sideBySideRenderingFunction); 15731 } 15732 15733 // Refresh to fix selection being off (#309) 15734 cm.refresh();
15735} 15736 15737 15738/** 15739 * Preview action. 15740 */ 15741function 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); 15751 } 15752 if(/editor-preview-active/.test(preview.className)) { 15753 preview.className = preview.className.replace( 15754 /\s*editor-preview-active\s*/g, "" 15755 ); 15756 if(toolbar) { 15757 toolbar.className = toolbar.className.replace(/\s*active\s*/g, ""); 15758 toolbar_div.className = toolbar_div.className.replace(/\s*disabled-for-preview*/g, ""); 15759 } 15760 } else { 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"; 15766 }, 1); 15767 if(toolbar) { 15768 toolbar.className += " active"; 15769 toolbar_div.className += " disabled-for-preview"; 15770 } 15771 } 15772 preview.innerHTML = editor.options.previewRender(editor.value(), preview); 15773 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); 15778} 15779 15780function _replaceSelection(cm, active, startEnd, url) { 15781 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) 15782 return; 15783 15784 var text; 15785 var start = startEnd[0]; 15786 var end = startEnd[1]; 15787 var startPoint = cm.getCursor("start"); 15788 var endPoint = cm.getCursor("end"); 15789 if(url) { 15790 end = end.replace("#url#", url); 15791 } 15792 if(active) { 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, 15798 ch: 0 15799 }); 15800 } else { 15801 text = cm.getSelection(); 15802 cm.replaceSelection(start + text + end); 15803 15804 startPoint.ch += start.length; 15805 if(startPoint !== endPoint) { 15806 endPoint.ch += start.length; 15807 } 15808 } 15809 cm.setSelection(startPoint, endPoint); 15810 cm.focus(); 15811} 15812 15813 15814function _toggleHeading(cm, direction, size) { 15815 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) 15816 return; 15817 15818 var startPoint = cm.getCursor("start"); 15819 var endPoint = cm.getCursor("end"); 15820 for(var i = startPoint.line; i <= endPoint.line; i++) { 15821 (function(i) { 15822 var text = cm.getLine(i); 15823 var currHeadingLevel = text.search(/[^#]/); 15824 15825 if(direction !== undefined) { 15826 if(currHeadingLevel <= 0) { 15827 if(direction == "bigger") { 15828 text = "###### " + text; 15829 } else { 15830 text = "# " + text; 15831 } 15832 } else if(currHeadingLevel == 6 && direction == "smaller") { 15833 text = text.substr(7); 15834 } else if(currHeadingLevel == 1 && direction == "bigger") { 15835 text = text.substr(2); 15836 } else { 15837 if(direction == "bigger") { 15838 text = text.substr(1); 15839 } else { 15840 text = "#" + text; 15841 } 15842 } 15843 } else { 15844 if(size == 1) { 15845 if(currHeadingLevel <= 0) { 15846 text = "# " + text; 15847 } else if(currHeadingLevel == size) { 15848 text = text.substr(currHeadingLevel + 1); 15849 } else { 15850 text = "# " + text.substr(currHeadingLevel + 1); 15851 } 15852 } else if(size == 2) { 15853 if(currHeadingLevel <= 0) { 15854 text = "## " + text; 15855 } else if(currHeadingLevel == size) { 15856 text = text.substr(currHeadingLevel + 1); 15857 } else { 15858 text = "## " + text.substr(currHeadingLevel + 1); 15859 } 15860 } else { 15861 if(currHeadingLevel <= 0) { 15862 text = "### " + text; 15863 } else if(currHeadingLevel == size) { 15864 text = text.substr(currHeadingLevel + 1); 15865 } else { 15866 text = "### " + text.substr(currHeadingLevel + 1); 15867 } 15868 } 15869 } 15870 15871 cm.replaceRange(text, { 15872 line: i, 15873 ch: 0 15874 }, { 15875 line: i, 15876 ch: 99999999999999 15877 }); 15878 })(i); 15879 } 15880 cm.focus(); 15881} 15882 15883 15884function _toggleLine(cm, name) { 15885 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) 15886 return; 15887 15888 var stat = getState(cm); 15889 var startPoint = cm.getCursor("start"); 15890 var endPoint = cm.getCursor("end"); 15891 var repl = { 15892 "quote": /^(\s*)\>\s+/, 15893 "unordered-list": /^(\s*)(\*|\-|\+)\s+/, 15894 "ordered-list": /^(\s*)\d+\.\s+/ 15895 }; 15896 var map = { 15897 "quote": "> ", 15898 "unordered-list": "* ", 15899 "ordered-list": "1. " 15900 }; 15901 for(var i = startPoint.line; i <= endPoint.line; i++) { 15902 (function(i) { 15903 var text = cm.getLine(i);
15904 if(stat[name]) { 15905 text = text.replace(repl[name], "$1"); 15906 } else { 15907 text = map[name] + text; 15908 } 15909 cm.replaceRange(text, { 15910 line: i, 15911 ch: 0 15912 }, { 15913 line: i, 15914 ch: 99999999999999 15915 }); 15916 })(i); 15917 } 15918 cm.focus(); 15919} 15920 15921function _toggleBlock(editor, type, start_chars, end_chars) { 15922 if(/editor-preview-active/.test(editor.codemirror.getWrapperElement().lastChild.className)) 15923 return; 15924 15925 end_chars = (typeof end_chars === "undefined") ? start_chars : end_chars; 15926 var cm = editor.codemirror; 15927 var stat = getState(cm); 15928 15929 var text; 15930 var start = start_chars; 15931 var end = end_chars; 15932 15933 var startPoint = cm.getCursor("start"); 15934 var endPoint = cm.getCursor("end"); 15935 15936 if(stat[type]) { 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(/(\*\*|~~)/, ""); 15949 } 15950 cm.replaceRange(start + end, { 15951 line: startPoint.line, 15952 ch: 0 15953 }, { 15954 line: startPoint.line, 15955 ch: 99999999999999 15956 }); 15957 15958 if(type == "bold" || type == "strikethrough") { 15959 startPoint.ch -= 2; 15960 if(startPoint !== endPoint) { 15961 endPoint.ch -= 2; 15962 } 15963 } else if(type == "italic") { 15964 startPoint.ch -= 1; 15965 if(startPoint !== endPoint) { 15966 endPoint.ch -= 1; 15967 } 15968 } 15969 } else { 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(""); 15979 } 15980 cm.replaceSelection(start + text + end); 15981 15982 startPoint.ch += start_chars.length; 15983 endPoint.ch = startPoint.ch + text.length; 15984 } 15985 15986 cm.setSelection(startPoint, endPoint); 15987 cm.focus(); 15988} 15989 15990function _cleanBlock(cm) { 15991 if(/editor-preview-active/.test(cm.getWrapperElement().lastChild.className)) 15992 return; 15993 15994 var startPoint = cm.getCursor("start"); 15995 var endPoint = cm.getCursor("end"); 15996 var text; 15997 15998 for(var line = startPoint.line; line <= endPoint.line; line++) { 15999 text = cm.getLine(line); 16000 text = text.replace(/^[ ]*([# ]+|\*|\-|[> ]+|[0-9]+(.|\)))[ ]*/, ""); 16001 16002 cm.replaceRange(text, { 16003 line: line, 16004 ch: 0 16005 }, { 16006 line: line, 16007 ch: 99999999999999 16008 }); 16009 } 16010} 16011 16012// Merge the properties of one object into another. 16013function _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] : []); 16018 } else if( 16019 source[property] !== null && 16020 typeof source[property] === "object" && 16021 source[property].constructor === Object 16022 ) { 16023 target[property] = _mergeProperties(target[property] || {}, source[property]); 16024 } else { 16025 target[property] = source[property]; 16026 } 16027 } 16028 } 16029 16030 return target; 16031} 16032 16033// Merge an arbitrary number of objects into one. 16034function extend(target) { 16035 for(var i = 1; i < arguments.length; i++) { 16036 target = _mergeProperties(target, arguments[i]); 16037 } 16038 16039 return target; 16040} 16041 16042/* The right word count in respect for CJK. */ 16043function 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); 16046 var count = 0; 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; 16051 } else { 16052 count += 1; 16053 } 16054 } 16055 return count; 16056} 16057 16058var toolbarBuiltInButtons = { 16059 "bold": { 16060 name: "bold", 16061 action: toggleBold, 16062 className: "fa fa-bold", 16063 title: "Bold", 16064 default: true 16065 }, 16066 "italic": { 16067 name: "italic", 16068 action: toggleItalic, 16069 className: "fa fa-italic", 16070 title: "Italic", 16071 default: true 16072 }, 16073 "strikethrough": { 16074 name: "strikethrough", 16075 action: toggleStrikethrough, 16076 className: "fa fa-strikethrough", 16077 title: "Strikethrough" 16078 }, 16079 "heading": { 16080 name: "heading", 16081 action: toggleHeadingSmaller, 16082 className: "fa fa-header", 16083 title: "Heading", 16084 default: true 16085 }, 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"
16091 }, 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" 16097 }, 16098 "heading-1": { 16099 name: "heading-1", 16100 action: toggleHeading1, 16101 className: "fa fa-header fa-header-x fa-header-1", 16102 title: "Big Heading" 16103 }, 16104 "heading-2": { 16105 name: "heading-2", 16106 action: toggleHeading2, 16107 className: "fa fa-header fa-header-x fa-header-2", 16108 title: "Medium Heading" 16109 }, 16110 "heading-3": { 16111 name: "heading-3", 16112 action: toggleHeading3, 16113 className: "fa fa-header fa-header-x fa-header-3", 16114 title: "Small Heading" 16115 }, 16116 "separator-1": { 16117 name: "separator-1" 16118 }, 16119 "code": { 16120 name: "code", 16121 action: toggleCodeBlock, 16122 className: "fa fa-code", 16123 title: "Code" 16124 }, 16125 "quote": { 16126 name: "quote", 16127 action: toggleBlockquote, 16128 className: "fa fa-quote-left", 16129 title: "Quote", 16130 default: true 16131 }, 16132 "unordered-list": { 16133 name: "unordered-list", 16134 action: toggleUnorderedList, 16135 className: "fa fa-list-ul", 16136 title: "Generic List", 16137 default: true 16138 }, 16139 "ordered-list": { 16140 name: "ordered-list", 16141 action: toggleOrderedList, 16142 className: "fa fa-list-ol", 16143 title: "Numbered List", 16144 default: true 16145 }, 16146 "clean-block": { 16147 name: "clean-block", 16148 action: cleanBlock, 16149 className: "fa fa-eraser fa-clean-block", 16150 title: "Clean block" 16151 }, 16152 "separator-2": { 16153 name: "separator-2" 16154 }, 16155 "link": { 16156 name: "link", 16157 action: drawLink, 16158 className: "fa fa-link", 16159 title: "Create Link", 16160 default: true 16161 }, 16162 "image": { 16163 name: "image", 16164 action: drawImage, 16165 className: "fa fa-picture-o", 16166 title: "Insert Image", 16167 default: true 16168 }, 16169 "table": { 16170 name: "table", 16171 action: drawTable, 16172 className: "fa fa-table", 16173 title: "Insert Table" 16174 }, 16175 "horizontal-rule": { 16176 name: "horizontal-rule", 16177 action: drawHorizontalRule, 16178 className: "fa fa-minus", 16179 title: "Insert Horizontal Line" 16180 }, 16181 "separator-3": { 16182 name: "separator-3" 16183 }, 16184 "preview": { 16185 name: "preview", 16186 action: togglePreview, 16187 className: "fa fa-eye no-disable", 16188 title: "Toggle Preview", 16189 default: true 16190 }, 16191 "side-by-side": { 16192 name: "side-by-side", 16193 action: toggleSideBySide, 16194 className: "fa fa-columns no-disable no-mobile", 16195 title: "Toggle Side by Side", 16196 default: true 16197 }, 16198 "fullscreen": { 16199 name: "fullscreen", 16200 action: toggleFullScreen, 16201 className: "fa fa-arrows-alt no-disable no-mobile", 16202 title: "Toggle Fullscreen", 16203 default: true 16204 }, 16205 "separator-4": { 16206 name: "separator-4" 16207 }, 16208 "guide": { 16209 name: "guide", 16210 action: "https://simplemde.com/markdown-guide", 16211 className: "fa fa-question-circle", 16212 title: "Markdown Guide", 16213 default: true 16214 }, 16215 "separator-5": { 16216 name: "separator-5" 16217 }, 16218 "undo": { 16219 name: "undo", 16220 action: undo, 16221 className: "fa fa-undo no-disable", 16222 title: "Undo" 16223 }, 16224 "redo": { 16225 name: "redo", 16226 action: redo, 16227 className: "fa fa-repeat no-disable", 16228 title: "Redo" 16229 } 16230}; 16231 16232var insertTexts = { 16233 link: ["[", "](#url#)"], 16234 image: [""], 16235 table: ["", "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n\n"], 16236 horizontalRule: ["", "\n\n-----\n\n"] 16237}; 16238 16239var promptTexts = { 16240 link: "URL for the link:", 16241 image: "URL of the image:" 16242}; 16243 16244var blockStyles = { 16245 "bold": "**", 16246 "code": "```", 16247 "italic": "*" 16248}; 16249 16250/** 16251 * Interface of SimpleMDE. 16252 */ 16253function SimpleMDE(options) { 16254 // Handle options parameter 16255 options = options || {}; 16256 16257 16258 // Used later to refer to it"s parent 16259 options.parent = this; 16260 16261 16262 // Check if Font Awesome needs to be auto downloaded 16263 var autoDownloadFA = true; 16264 16265 if(options.autoDownloadFontAwesome === false) { 16266 autoDownloadFA = false; 16267 } 16268 16269 if(options.autoDownloadFontAwesome !== true) { 16270 var styleSheets = document.styleSheets; 16271 for(var i = 0; i < styleSheets.length; i++) { 16272 if(!styleSheets[i].href) 16273 continue; 16274 16275 if(styleSheets[i].href.indexOf("//maxcdn.bootstrapcdn.com/font-awesome/") > -1) { 16276 autoDownloadFA = false; 16277 } 16278 } 16279 } 16280 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); 16286 } 16287 16288 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."); 16295 return; 16296 } 16297 16298 16299 // Handle toolbar 16300 if(options.toolbar === undefined) { 16301 // Initialize 16302 options.toolbar = []; 16303 16304 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("|"); 16310 } 16311 16312 if(toolbarBuiltInButtons[key].default === true || (options.showIcons && options.showIcons.constructor === Array && options.showIcons.indexOf(key) != -1)) { 16313 options.toolbar.push(key); 16314 } 16315 } 16316 } 16317 } 16318 16319 16320 // Handle status bar 16321 if(!options.hasOwnProperty("status")) { 16322 options.status = ["autosave", "lines", "words", "cursor"]; 16323 } 16324 16325 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); 16331 }; 16332 } 16333 16334 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 || {}); 16339 16340 16341 // Merging the insertTexts, with the given options 16342 options.insertTexts = extend({}, insertTexts, options.insertTexts || {}); 16343 16344 16345 // Merging the promptTexts, with the given options 16346 options.promptTexts = promptTexts; 16347 16348 16349 // Merging the blockStyles, with the given options 16350 options.blockStyles = extend({}, blockStyles, options.blockStyles || {}); 16351 16352 16353 // Merging the shortcuts, with the given options 16354 options.shortcuts = extend({}, shortcuts, options.shortcuts || {}); 16355 16356 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; 16360 16361
16362 // Update this options 16363 this.options = options; 16364 16365 16366 // Auto render 16367 this.render(); 16368 16369 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); 16375 } 16376} 16377 16378/** 16379 * Default markdown render. 16380 */ 16381SimpleMDE.prototype.markdown = function(text) { 16382 if(marked) { 16383 // Initialize 16384 var markedOptions = {}; 16385 16386 16387 // Update options 16388 if(this.options && this.options.renderingConfig && this.options.renderingConfig.singleLineBreaks === false) { 16389 markedOptions.breaks = false; 16390 } else { 16391 markedOptions.breaks = true; 16392 } 16393 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; 16397 }; 16398 } 16399 16400 16401 // Set options 16402 marked.setOptions(markedOptions); 16403 16404 16405 // Return 16406 return marked(text); 16407 } 16408}; 16409 16410/** 16411 * Render editor to the given element. 16412 */ 16413SimpleMDE.prototype.render = function(el) { 16414 if(!el) { 16415 el = this.element || document.getElementsByTagName("textarea")[0]; 16416 } 16417 16418 if(this._rendered && this._rendered === el) { 16419 // Already rendered. 16420 return; 16421 } 16422 16423 this.element = el; 16424 var options = this.options; 16425 16426 var self = this; 16427 var keyMaps = {}; 16428 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) { 16432 (function(key) {
16433 keyMaps[fixShortcut(options.shortcuts[key])] = function() { 16434 bindings[key](self); 16435 }; 16436 })(key); 16437 } 16438 } 16439 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); 16445 }; 16446 16447 document.addEventListener("keydown", function(e) { 16448 e = e || window.event; 16449 16450 if(e.keyCode == 27) { 16451 if(self.codemirror.getOption("fullScreen")) toggleFullScreen(self); 16452 } 16453 }, false); 16454 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; 16461 16462 CodeMirrorSpellChecker({ 16463 codeMirrorInstance: CodeMirror 16464 }); 16465 } else { 16466 mode = options.parsingConfig; 16467 mode.name = "gfm"; 16468 mode.gitHubSpice = false; 16469 } 16470 16471 this.codemirror = CodeMirror.fromTextArea(el, { 16472 mode: mode, 16473 backdrop: backdrop, 16474 theme: "paper", 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 16485 }); 16486 16487 if(options.forceSync === true) { 16488 var cm = this.codemirror; 16489 cm.on("change", function() { 16490 cm.save(); 16491 }); 16492 } 16493 16494 this.gui = {}; 16495 16496 if(options.toolbar !== false) { 16497 this.gui.toolbar = this.createToolbar(); 16498 } 16499 if(options.status !== false) { 16500 this.gui.statusbar = this.createStatusbar(); 16501 } 16502 if(options.autosave != undefined && options.autosave.enabled === true) { 16503 this.autosave(); 16504 } 16505 16506 this.gui.sideBySide = this.createSideBySide(); 16507 16508 this._rendered = this.element; 16509 16510 16511 // Fixes CodeMirror bug (#344) 16512 var temp_cm = this.codemirror; 16513 setTimeout(function() { 16514 temp_cm.refresh(); 16515 }.bind(temp_cm), 0); 16516}; 16517 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. 16519function isLocalStorageAvailable() { 16520 if(typeof localStorage === "object") { 16521 try { 16522 localStorage.setItem("smde_localStorage", 1); 16523 localStorage.removeItem("smde_localStorage"); 16524 } catch(e) { 16525 return false; 16526 } 16527 } else { 16528 return false; 16529 } 16530 16531 return true; 16532} 16533 16534SimpleMDE.prototype.autosave = function() { 16535 if(isLocalStorageAvailable()) { 16536 var simplemde = this; 16537 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"); 16540 return; 16541 } 16542 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); 16546 }); 16547 } 16548 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; 16553 } 16554 16555 this.options.autosave.loaded = true; 16556 } 16557 16558 localStorage.setItem("smde_" + this.options.autosave.uniqueId, simplemde.value()); 16559 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(); 16565 var dd = "am"; 16566 var h = hh; 16567 if(h >= 12) { 16568 h = hh - 12; 16569 dd = "pm"; 16570 } 16571 if(h == 0) { 16572 h = 12; 16573 } 16574 m = m < 10 ? "0" + m : m; 16575 16576 el.innerHTML = "Autosaved: " + h + ":" + m + " " + dd; 16577 } 16578 16579 this.autosaveTimeoutId = setTimeout(function() { 16580 simplemde.autosave(); 16581 }, this.options.autosave.delay || 10000); 16582 } else { 16583 console.log("SimpleMDE: localStorage not available, cannot autosave"); 16584 } 16585}; 16586 16587SimpleMDE.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"); 16591 return; 16592 } 16593 16594 localStorage.removeItem("smde_" + this.options.autosave.uniqueId); 16595 } else { 16596 console.log("SimpleMDE: localStorage not available, cannot autosave"); 16597 } 16598}; 16599 16600SimpleMDE.prototype.createSideBySide = function() {
16601 var cm = this.codemirror; 16602 var wrapper = cm.getWrapperElement(); 16603 var preview = wrapper.nextSibling; 16604 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); 16609 } 16610 16611 // Syncs scroll editor -> preview 16612 var cScroll = false; 16613 var pScroll = false; 16614 cm.on("scroll", function(v) { 16615 if(cScroll) { 16616 cScroll = false; 16617 return; 16618 } 16619 pScroll = true; 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; 16624 }); 16625 16626 // Syncs scroll preview -> editor 16627 preview.onscroll = function() { 16628 if(pScroll) { 16629 pScroll = false; 16630 return; 16631 } 16632 cScroll = true; 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); 16637 }; 16638 return preview; 16639}; 16640 16641SimpleMDE.prototype.createToolbar = function(items) { 16642 items = items || this.options.toolbar; 16643 16644 if(!items || items.length === 0) { 16645 return; 16646 } 16647 var i; 16648 for(i = 0; i < items.length; i++) { 16649 if(toolbarBuiltInButtons[items[i]] != undefined) { 16650 items[i] = toolbarBuiltInButtons[items[i]]; 16651 } 16652 } 16653 16654 var bar = document.createElement("div"); 16655 bar.className = "editor-toolbar"; 16656 16657 var self = this; 16658 16659 var toolbarData = {}; 16660 self.toolbar = items; 16661 16662 for(i = 0; i < items.length; i++) { 16663 if(items[i].name == "guide" && self.options.toolbarGuideIcon === false) 16664 continue; 16665 16666 if(self.options.hideIcons && self.options.hideIcons.indexOf(items[i].name) != -1) 16667 continue; 16668 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()) 16672 continue; 16673 16674 16675 // Don't include trailing separators 16676 if(items[i] === "|") { 16677 var nonSeparatorIconsFollow = false; 16678 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; 16682 } 16683 } 16684 16685 if(!nonSeparatorIconsFollow) 16686 continue; 16687 } 16688 16689 16690 // Create the icon and append to the toolbar 16691 (function(item) { 16692 var el; 16693 if(item === "|") { 16694 el = createSep(); 16695 } else { 16696 el = createIcon(item, self.options.toolbarTips, self.options.shortcuts); 16697 } 16698 16699 // bind events, special for info 16700 if(item.action) { 16701 if(typeof item.action === "function") { 16702 el.onclick = function(e) { 16703 e.preventDefault(); 16704 item.action(self); 16705 }; 16706 } else if(typeof item.action === "string") { 16707 el.href = item.action; 16708 el.target = "_blank"; 16709 } 16710 } 16711 16712 toolbarData[item.name || item] = el; 16713 bar.appendChild(el); 16714 })(items[i]); 16715 } 16716 16717 self.toolbarElements = toolbarData; 16718 16719 var cm = this.codemirror; 16720 cm.on("cursorActivity", function() { 16721 var stat = getState(cm); 16722 16723 for(var key in toolbarData) { 16724 (function(key) { 16725 var el = toolbarData[key]; 16726 if(stat[key]) { 16727 el.className += " active"; 16728 } else if(key != "fullscreen" && key != "side-by-side") { 16729 el.className = el.className.replace(/\s*active\s*/g, ""); 16730 } 16731 })(key); 16732 } 16733 }); 16734 16735 var cmWrapper = cm.getWrapperElement(); 16736 cmWrapper.parentNode.insertBefore(bar, cmWrapper); 16737 return bar; 16738}; 16739 16740SimpleMDE.prototype.createStatusbar = function(status) { 16741 // Initialize 16742 status = status || this.options.status; 16743 var options = this.options; 16744 var cm = this.codemirror; 16745 16746 16747 // Make sure the status variable is valid 16748 if(!status || status.length === 0) 16749 return; 16750 16751 16752 // Set up the built-in items 16753 var items = []; 16754 var i, onUpdate, defaultValue; 16755 16756 for(i = 0; i < status.length; i++) { 16757 // Reset some values 16758 onUpdate = undefined; 16759 defaultValue = undefined; 16760 16761 16762 // Handle if custom or not 16763 if(typeof status[i] === "object") { 16764 items.push({ 16765 className: status[i].className, 16766 defaultValue: status[i].defaultValue, 16767 onUpdate: status[i].onUpdate 16768 }); 16769 } else { 16770 var name = status[i]; 16771 16772 if(name === "words") { 16773 defaultValue = function(el) { 16774 el.innerHTML = wordCount(cm.getValue()); 16775 }; 16776 onUpdate = function(el) { 16777 el.innerHTML = wordCount(cm.getValue()); 16778 }; 16779 } else if(name === "lines") { 16780 defaultValue = function(el) { 16781 el.innerHTML = cm.lineCount(); 16782 }; 16783 onUpdate = function(el) { 16784 el.innerHTML = cm.lineCount(); 16785 }; 16786 } else if(name === "cursor") { 16787 defaultValue = function(el) { 16788 el.innerHTML = "0:0"; 16789 }; 16790 onUpdate = function(el) { 16791 var pos = cm.getCursor(); 16792 el.innerHTML = pos.line + ":" + pos.ch; 16793 }; 16794 } else if(name === "autosave") { 16795 defaultValue = function(el) { 16796 if(options.autosave != undefined && options.autosave.enabled === true) { 16797 el.setAttribute("id", "autosaved"); 16798 } 16799 }; 16800 } 16801 16802 items.push({ 16803 className: name, 16804 defaultValue: defaultValue, 16805 onUpdate: onUpdate 16806 }); 16807 } 16808 } 16809 16810 16811 // Create element for the status bar 16812 var bar = document.createElement("div"); 16813 bar.className = "editor-statusbar"; 16814 16815 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]; 16820 16821 16822 // Create span element 16823 var el = document.createElement("span"); 16824 el.className = item.className; 16825 16826 16827 // Ensure the defaultValue is a function 16828 if(typeof item.defaultValue === "function") { 16829 item.defaultValue(el); 16830 } 16831 16832 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() { 16838 item.onUpdate(el); 16839 }; 16840 }(el, item))); 16841 } 16842 16843 16844 // Append the item to the status bar 16845 bar.appendChild(el); 16846 } 16847 16848 16849 // Insert the status bar into the DOM
16850 var cmWrapper = this.codemirror.getWrapperElement(); 16851 cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling); 16852 return bar; 16853}; 16854 16855/** 16856 * Get or set the text content. 16857 */ 16858SimpleMDE.prototype.value = function(val) { 16859 if(val === undefined) { 16860 return this.codemirror.getValue(); 16861 } else { 16862 this.codemirror.getDoc().setValue(val); 16863 return this; 16864 } 16865}; 16866 16867 16868/** 16869 * Bind static methods for exports. 16870 */ 16871SimpleMDE.toggleBold = toggleBold; 16872SimpleMDE.toggleItalic = toggleItalic; 16873SimpleMDE.toggleStrikethrough = toggleStrikethrough; 16874SimpleMDE.toggleBlockquote = toggleBlockquote; 16875SimpleMDE.toggleHeadingSmaller = toggleHeadingSmaller; 16876SimpleMDE.toggleHeadingBigger = toggleHeadingBigger; 16877SimpleMDE.toggleHeading1 = toggleHeading1; 16878SimpleMDE.toggleHeading2 = toggleHeading2; 16879SimpleMDE.toggleHeading3 = toggleHeading3; 16880SimpleMDE.toggleCodeBlock = toggleCodeBlock; 16881SimpleMDE.toggleUnorderedList = toggleUnorderedList; 16882SimpleMDE.toggleOrderedList = toggleOrderedList; 16883SimpleMDE.cleanBlock = cleanBlock; 16884SimpleMDE.drawLink = drawLink; 16885SimpleMDE.drawImage = drawImage; 16886SimpleMDE.drawTable = drawTable; 16887SimpleMDE.drawHorizontalRule = drawHorizontalRule; 16888SimpleMDE.undo = undo; 16889SimpleMDE.redo = redo; 16890SimpleMDE.togglePreview = togglePreview; 16891SimpleMDE.toggleSideBySide = toggleSideBySide; 16892SimpleMDE.toggleFullScreen = toggleFullScreen; 16893 16894/** 16895 * Bind instance methods for exports. 16896 */ 16897SimpleMDE.prototype.toggleBold = function() { 16898 toggleBold(this); 16899}; 16900SimpleMDE.prototype.toggleItalic = function() { 16901 toggleItalic(this); 16902}; 16903SimpleMDE.prototype.toggleStrikethrough = function() { 16904 toggleStrikethrough(this); 16905}; 16906SimpleMDE.prototype.toggleBlockquote = function() { 16907 toggleBlockquote(this); 16908}; 16909SimpleMDE.prototype.toggleHeadingSmaller = function() { 16910 toggleHeadingSmaller(this); 16911}; 16912SimpleMDE.prototype.toggleHeadingBigger = function() { 16913 toggleHeadingBigger(this); 16914}; 16915SimpleMDE.prototype.toggleHeading1 = function() { 16916 toggleHeading1(this); 16917}; 16918SimpleMDE.prototype.toggleHeading2 = function() { 16919 toggleHeading2(this); 16920}; 16921SimpleMDE.prototype.toggleHeading3 = function() { 16922 toggleHeading3(this); 16923}; 16924SimpleMDE.prototype.toggleCodeBlock = function() { 16925 toggleCodeBlock(this); 16926}; 16927SimpleMDE.prototype.toggleUnorderedList = function() { 16928 toggleUnorderedList(this); 16929}; 16930SimpleMDE.prototype.toggleOrderedList = function() { 16931 toggleOrderedList(this); 16932}; 16933SimpleMDE.prototype.cleanBlock = function() { 16934 cleanBlock(this); 16935}; 16936SimpleMDE.prototype.drawLink = function() { 16937 drawLink(this); 16938}; 16939SimpleMDE.prototype.drawImage = function() { 16940 drawImage(this); 16941}; 16942SimpleMDE.prototype.drawTable = function() { 16943 drawTable(this); 16944}; 16945SimpleMDE.prototype.drawHorizontalRule = function() { 16946 drawHorizontalRule(this); 16947}; 16948SimpleMDE.prototype.undo = function() { 16949 undo(this); 16950}; 16951SimpleMDE.prototype.redo = function() { 16952 redo(this); 16953}; 16954SimpleMDE.prototype.togglePreview = function() { 16955 togglePreview(this); 16956}; 16957SimpleMDE.prototype.toggleSideBySide = function() { 16958 toggleSideBySide(this); 16959}; 16960SimpleMDE.prototype.toggleFullScreen = function() { 16961 toggleFullScreen(this); 16962}; 16963 16964SimpleMDE.prototype.isPreviewActive = function() { 16965 var cm = this.codemirror; 16966 var wrapper = cm.getWrapperElement(); 16967 var preview = wrapper.lastChild; 16968 16969 return /editor-preview-active/.test(preview.className); 16970}; 16971 16972SimpleMDE.prototype.isSideBySideActive = function() { 16973 var cm = this.codemirror; 16974 var wrapper = cm.getWrapperElement(); 16975 var preview = wrapper.nextSibling; 16976 16977 return /editor-preview-active-side/.test(preview.className); 16978}; 16979 16980SimpleMDE.prototype.isFullscreenActive = function() { 16981 var cm = this.codemirror; 16982 16983 return cm.getOption("fullScreen"); 16984}; 16985 16986SimpleMDE.prototype.getState = function() { 16987 var cm = this.codemirror; 16988 16989 return getState(cm); 16990}; 16991 16992SimpleMDE.prototype.toTextArea = function() { 16993 var cm = this.codemirror; 16994 var wrapper = cm.getWrapperElement(); 16995 16996 if(wrapper.parentNode) { 16997 if(this.gui.toolbar) { 16998 wrapper.parentNode.removeChild(this.gui.toolbar); 16999 } 17000 if(this.gui.statusbar) { 17001 wrapper.parentNode.removeChild(this.gui.statusbar); 17002 } 17003 if(this.gui.sideBySide) { 17004 wrapper.parentNode.removeChild(this.gui.sideBySide); 17005 } 17006 } 17007 17008 cm.toTextArea(); 17009 17010 if(this.autosaveTimeoutId) { 17011 clearTimeout(this.autosaveTimeoutId); 17012 this.autosaveTimeoutId = undefined; 17013 this.clearAutosavedValue(); 17014 } 17015}; 17016 17017module.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) 17019});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.