Ticket #12423: 12423.codemirror.unminified.diff
File 12423.codemirror.unminified.diff, 751.2 KB (added by , 8 years ago) |
---|
-
src/wp-admin/css/common.css
2258 2258 visibility: visible; 2259 2259 } 2260 2260 2261 #template div {2261 #template > div { 2262 2262 margin-right: 190px; 2263 2263 } 2264 2264 -
src/wp-admin/theme-editor.php
106 106 validate_file_to_edit( $file, $allowed_files ); 107 107 $scrollto = isset( $_REQUEST['scrollto'] ) ? (int) $_REQUEST['scrollto'] : 0; 108 108 109 $codemirror_opts = false; 110 switch ( @pathinfo( $file, PATHINFO_EXTENSION ) ) { 111 case 'css' : 112 wp_enqueue_script( 'codemirror-mode-css' ); 113 wp_enqueue_style( 'codemirror' ); 114 $codemirror_opts = array( 115 'inputStyle' => 'contenteditable', 116 'lineNumbers' => true, 117 'mode' => 'text/css', 118 ); 119 break; 120 case 'php' : 121 wp_enqueue_script( 'codemirror-mode-html' ); 122 wp_enqueue_script( 'codemirror-mode-xml' ); 123 wp_enqueue_script( 'codemirror-mode-javascript' ); 124 wp_enqueue_script( 'codemirror-mode-css' ); 125 wp_enqueue_script( 'codemirror-mode-php' ); 126 wp_enqueue_style( 'codemirror' ); 127 $codemirror_opts = array( 128 'inputStyle' => 'contenteditable', 129 'lineNumbers' => true, 130 'mode' => 'application/x-httpd-php', 131 'indentUnit' => 4, 132 'indentWithTabs' => true, 133 ); 134 break; 135 default : 136 break; 137 } 138 /** 139 * Give folks a chance to filter the arguments passed to CodeMirror -- This will let them enable 140 * or disable it (by returning something that evaluates to false) as they choose as well. 141 * 142 * @param $codemirror_opts The array of options to be passed to codemirror. Falsey doesn't use codemirror. 143 * @param $file The file being displayed. 144 * @param $theme The WP_Theme object for the current theme being edited. 145 */ 146 $codemirror_opts = apply_filters( 'theme_editor_codemirror_opts', $codemirror_opts, $file, $theme ); 147 109 148 switch( $action ) { 110 149 case 'update': 111 150 check_admin_referer( 'edit-theme_' . $file . $stylesheet ); … … 289 328 jQuery(document).ready(function($){ 290 329 $('#template').submit(function(){ $('#scrollto').val( $('#newcontent').scrollTop() ); }); 291 330 $('#newcontent').scrollTop( $('#scrollto').val() ); 331 /** CODEMIRROR */ 332 <?php if ( $codemirror_opts ) : ?> 333 wp.codemirror = CodeMirror.fromTextArea( document.getElementById('newcontent'), <?php echo json_encode( $codemirror_opts ); ?> ); 334 <?php endif; ?> 292 335 }); 293 336 </script> 294 337 <?php -
src/wp-includes/js/codemirror/AUTHORS
1 List of CodeMirror contributors. Updated before every release. 2 3 4r2r 4 Aaron Brooks 5 Abdelouahab 6 Abe Fettig 7 Adam Ahmed 8 Adam King 9 adanlobato 10 Adán Lobato 11 Adrian Aichner 12 Adrian Heine 13 Adrien Bertrand 14 aeroson 15 Ahmad Amireh 16 Ahmad M. Zawawi 17 ahoward 18 Akeksandr Motsjonov 19 Alasdair Smith 20 Alberto González Palomo 21 Alberto Pose 22 Albert Xing 23 Alexander Pavlov 24 Alexander Schepanovski 25 Alexander Shvets 26 Alexander Solovyov 27 Alexandre Bique 28 alexey-k 29 Alex Piggott 30 Aliaksei Chapyzhenka 31 Allen Sarkisyan 32 Amin Shali 33 Amin Ullah Khan 34 amshali@google.com 35 Amsul 36 amuntean 37 Amy 38 Ananya Sen 39 anaran 40 AndersMad 41 Anders Nawroth 42 Anderson Mesquita 43 Anders Wåglund 44 Andrea G 45 Andreas Reischuck 46 Andres Taylor 47 Andre von Houck 48 Andrew Cheng 49 Andrey Fedorov 50 Andrey Klyuchnikov 51 Andrey Lushnikov 52 Andrey Shchekin 53 Andy Joslin 54 Andy Kimball 55 Andy Li 56 Angelo 57 angelozerr 58 angelo.zerr@gmail.com 59 Ankit 60 Ankit Ahuja 61 Ansel Santosa 62 Anthony Dugois 63 anthonygego 64 Anthony Gégo 65 Anthony Grimes 66 Anton Kovalyov 67 Apollo Zhu 68 AQNOUCH Mohammed 69 areos 70 Arnab Bose 71 as3boyan 72 atelierbram 73 AtomicPages LLC 74 Atul Bhouraskar 75 Aurelian Oancea 76 Barret Rennie 77 Basarat Ali Syed 78 Bastian Müller 79 belhaj 80 Bem Jones-Bey 81 benbro 82 Beni Cherniavsky-Paskin 83 Benjamin DeCoste 84 Ben Keen 85 Ben Miller 86 Ben Mosher 87 Bernhard Sirlinger 88 Bert Chang 89 Bharad 90 BigBlueHat 91 Billy Moon 92 binny 93 B Krishna Chaitanya 94 Blaine G 95 blukat29 96 boomyjee 97 borawjm 98 Brad Metcalf 99 Brandon Frohs 100 Brandon Wamboldt 101 Brett Zamir 102 Brian Grinstead 103 Brian Sletten 104 Bruce Mitchener 105 Caitlin Potter 106 Calin Barbat 107 callodacity 108 Camilo Roca 109 Chad Jolly 110 Chandra Sekhar Pydi 111 Charles Skelton 112 Cheah Chu Yeow 113 Chris Coyier 114 Chris Ford 115 Chris Granger 116 Chris Houseknecht 117 Chris Lohfink 118 Chris Morgan 119 Chris Smith 120 Christian Oyarzun 121 Christian Petrov 122 Christopher Brown 123 Christopher Mitchell 124 Christopher Pfohl 125 Chunliang Lyu 126 ciaranj 127 CodeAnimal 128 coderaiser 129 Cole R Lawrence 130 ComFreek 131 Curtis Gagliardi 132 dagsta 133 daines 134 Dale Jung 135 Dan Bentley 136 Dan Heberden 137 Daniel, Dao Quang Minh 138 Daniele Di Sarli 139 Daniel Faust 140 Daniel Huigens 141 Daniel Kesler 142 Daniel KJ 143 Daniel Neel 144 Daniel Parnell 145 Danny Yoo 146 darealshinji 147 Darius Roberts 148 Dave Brondsema 149 Dave Myers 150 David Barnett 151 David H. Bronke 152 David Mignot 153 David Pathakjee 154 David Vázquez 155 David Whittington 156 deebugger 157 Deep Thought 158 Devin Abbott 159 Devon Carew 160 Dick Choi 161 dignifiedquire 162 Dimage Sapelkin 163 Dmitry Kiselyov 164 domagoj412 165 Dominator008 166 Domizio Demichelis 167 Doug Wikle 168 Drew Bratcher 169 Drew Hintz 170 Drew Khoury 171 Drini Cami 172 Dror BG 173 duralog 174 eborden 175 edsharp 176 ekhaled 177 Elisée 178 Emmanuel Schanzer 179 Enam Mijbah Noor 180 Eric Allam 181 Erik Welander 182 eustas 183 Fabien O'Carroll 184 Fabio Zendhi Nagao 185 Faiza Alsaied 186 Fauntleroy 187 fbuchinger 188 feizhang365 189 Felipe Lalanne 190 Felix Raab 191 ficristo 192 Filip Noetzel 193 Filip Stollár 194 flack 195 ForbesLindesay 196 Forbes Lindesay 197 Ford_Lawnmower 198 Forrest Oliphant 199 Frank Wiegand 200 Gabriel Gheorghian 201 Gabriel Horner 202 Gabriel Nahmias 203 galambalazs 204 Gary Sheng 205 Gautam Mehta 206 Gavin Douglas 207 gekkoe 208 geowarin 209 Gerard Braad 210 Gergely Hegykozi 211 Giovanni Calò 212 Glebov Boris 213 Glenn Jorde 214 Glenn Ruehle 215 Golevka 216 Google Inc. 217 Gordon Smith 218 Grant Skinner 219 greengiant 220 Gregory Koberger 221 Grzegorz Mazur 222 Guillaume Massé 223 Guillaume Massé 224 guraga 225 Gustavo Rodrigues 226 Hakan Tunc 227 Hans Engel 228 Hardest 229 Harshvardhan Gupta 230 Hasan Karahan 231 Hector Oswaldo Caballero 232 Hendrik Wallbaum 233 Herculano Campos 234 Hiroyuki Makino 235 hitsthings 236 Hocdoc 237 Hugues Malphettes 238 Ian Beck 239 Ian Dickinson 240 Ian Wehrman 241 Ian Wetherbee 242 Ice White 243 ICHIKAWA, Yuji 244 idleberg 245 ilvalle 246 Ingo Richter 247 Irakli Gozalishvili 248 Ivan Kurnosov 249 Ivoah 250 Jacob Lee 251 Jake Peyser 252 Jakob Miland 253 Jakub Vrana 254 Jakub Vrána 255 James Campos 256 James Howard 257 James Thorne 258 Jamie Hill 259 Jan Jongboom 260 jankeromnes 261 Jan Keromnes 262 Jan Odvarko 263 Jan Schär 264 Jan T. Sott 265 Jared Dean 266 Jared Forsyth 267 Jared Jacobs 268 Jason 269 Jason Barnabe 270 Jason Grout 271 Jason Johnston 272 Jason San Jose 273 Jason Siefken 274 Jaydeep Solanki 275 Jean Boussier 276 Jeff Blaisdell 277 Jeff Jenkins 278 jeffkenton 279 Jeff Pickhardt 280 jem (graphite) 281 Jeremy Parmenter 282 Jim 283 Jim Avery 284 JobJob 285 jochenberger 286 Jochen Berger 287 Joel Einbinder 288 joelpinheiro 289 Johan Ask 290 John Connor 291 John-David Dalton 292 John Engler 293 John Lees-Miller 294 John Snelson 295 John Van Der Loo 296 Jon Ander Peñalba 297 Jonas Döbertin 298 Jonathan Malmaud 299 Jon Gacnik 300 jongalloway 301 Jon Malmaud 302 Jon Sangster 303 Joost-Wim Boekesteijn 304 Joseph Pecoraro 305 Josh Cohen 306 Josh Soref 307 Joshua Newman 308 Josh Watzman 309 jots 310 jsoojeon 311 ju1ius 312 Juan Benavides Romero 313 Jucovschi Constantin 314 Juho Vuori 315 Julien Rebetez 316 Justin Andresen 317 Justin Hileman 318 jwallers@gmail.com 319 kaniga 320 karevn 321 Kayur Patel 322 Kazuhito Hokamura 323 Ken Newman 324 ken restivo 325 Ken Rockot 326 Kevin Earls 327 Kevin Sawicki 328 Kevin Ushey 329 Klaus Silveira 330 Koh Zi Han, Cliff 331 komakino 332 Konstantin Lopuhin 333 koops 334 Kris Ciccarello 335 ks-ifware 336 kubelsmieci 337 KwanEsq 338 Kyle Kelley 339 Lanfei 340 Lanny 341 Laszlo Vidacs 342 leaf corcoran 343 Leonid Khachaturov 344 Leon Sorokin 345 Leonya Khachaturov 346 Liam Newman 347 Libo Cannici 348 LloydMilligan 349 LM 350 lochel 351 Lorenzo Stoakes 352 Luciano Longo 353 Lu Fangjian 354 Luke Browning 355 Luke Granger-Brown 356 Luke Stagner 357 lynschinzer 358 M1cha 359 Madhura Jayaratne 360 Maksim Lin 361 Maksym Taran 362 Malay Majithia 363 Manideep 364 Manuel Rego Casasnovas 365 Marat Dreizin 366 Marcel Gerber 367 Marcelo Camargo 368 Marco Aurélio 369 Marco Munizaga 370 Marcus Bointon 371 Marek Rudnicki 372 Marijn Haverbeke 373 Mário Gonçalves 374 Mario Pietsch 375 Mark Anderson 376 Mark Lentczner 377 Marko Bonaci 378 Mark Peace 379 Markus Bordihn 380 Martin Balek 381 Martín Gaitán 382 Martin Hasoň 383 Martin Hunt 384 Martin Laine 385 Martin Zagora 386 Mason Malone 387 Mateusz Paprocki 388 Mathias Bynens 389 mats cronqvist 390 Matt Gaide 391 Matthew Bauer 392 Matthew Beale 393 matthewhayes 394 Matthew Rathbone 395 Matthias Bussonnier 396 Matthias BUSSONNIER 397 Matt McDonald 398 Matt Pass 399 Matt Sacks 400 mauricio 401 Maximilian Hils 402 Maxim Kraev 403 Max Kirsch 404 Max Schaefer 405 Max Xiantu 406 mbarkhau 407 McBrainy 408 melpon 409 Metatheos 410 Micah Dubinko 411 Michael 412 Michael Goderbauer 413 Michael Grey 414 Michael Kaminsky 415 Michael Lehenbauer 416 Michael Zhou 417 Michal Dorner 418 Mighty Guava 419 Miguel Castillo 420 mihailik 421 Mike 422 Mike Brevoort 423 Mike Diaz 424 Mike Ivanov 425 Mike Kadin 426 Mike Kobit 427 MinRK 428 Miraculix87 429 misfo 430 mkaminsky11 431 mloginov 432 Moritz Schwörer 433 mps 434 ms 435 mtaran-google 436 Mu-An Chiou 437 Narciso Jaramillo 438 Nathan Williams 439 ndr 440 nerbert 441 nextrevision 442 ngn 443 nguillaumin 444 Ng Zhi An 445 Nicholas Bollweg 446 Nicholas Bollweg (Nick) 447 Nick Kreeger 448 Nick Small 449 Nicolò Ribaudo 450 Niels van Groningen 451 nightwing 452 Nikita Beloglazov 453 Nikita Vasilyev 454 Nikolay Kostov 455 nilp0inter 456 Nisarg Jhaveri 457 nlwillia 458 noragrossman 459 Norman Rzepka 460 Oreoluwa Onatemowo 461 Oskar Segersvärd 462 pablo 463 pabloferz 464 Page 465 Panupong Pasupat 466 paris 467 Paris 468 Paris Kasidiaris 469 Patil Arpith 470 Patrick Stoica 471 Patrick Strawderman 472 Paul Garvin 473 Paul Ivanov 474 Paul Masson 475 Pavel 476 Pavel Feldman 477 Pavel Petržela 478 Pavel Strashkin 479 Paweł Bartkiewicz 480 peteguhl 481 peter 482 Peter Flynn 483 peterkroon 484 Peter Kroon 485 Philipp A 486 Philip Stadermann 487 Pierre Gerold 488 Piët Delport 489 Pontus Melke 490 prasanthj 491 Prasanth J 492 Prayag Verma 493 Radek Piórkowski 494 Rahul 495 Rahul Anand 496 ramwin1 497 Randall Mason 498 Randy Burden 499 Randy Edmunds 500 Rasmus Erik Voel Jensen 501 ray ratchup 502 Ray Ratchup 503 Remi Nyborg 504 Richard Denton 505 Richard van der Meer 506 Richard Z.H. Wang 507 Rishi Goomar 508 Robert Crossfield 509 Roberto Abdelkader Martínez Pérez 510 robertop23 511 Robert Plummer 512 Rrandom 513 Ruslan Osmanov 514 Ryan Prior 515 sabaca 516 Sam Lee 517 Samuel Ainsworth 518 Sam Wilson 519 sandeepshetty 520 Sander AKA Redsandro 521 Sander Verweij 522 santec 523 Sascha Peilicke 524 satamas 525 satchmorun 526 sathyamoorthi 527 S. Chris Colbert 528 SCLINIC\jdecker 529 Scott Aikin 530 Scott Goodhew 531 Sebastian Zaha 532 Sergey Goder 533 Sergey Tselovalnikov 534 Se-Won Kim 535 shaund 536 shaun gilchrist 537 Shawn A 538 Shea Bunge 539 sheopory 540 Shiv Deepak 541 Shmuel Englard 542 Shubham Jain 543 Siamak Mokhtari 544 silverwind 545 sinkuu 546 snasa 547 soliton4 548 sonson 549 spastorelli 550 srajanpaliwal 551 Stanislav Oaserele 552 Stas Kobzar 553 Stefan Borsje 554 Steffen Beyer 555 Steffen Bruchmann 556 Stephen Lavelle 557 Steve Champagne 558 Steve Hoover 559 Steve O'Hara 560 stoskov 561 Stu Kennedy 562 Sungho Kim 563 sverweij 564 Taha Jahangir 565 takamori 566 Tako Schotanus 567 Takuji Shimokawa 568 Tarmil 569 TDaglis 570 tel 571 tfjgeorge 572 Thaddee Tyl 573 thanasis 574 TheHowl 575 themrmax 576 think 577 Thomas Dvornik 578 Thomas Kluyver 579 Thomas Schmid 580 Tim Alby 581 Tim Baumann 582 Timothy Farrell 583 Timothy Gu 584 Timothy Hatcher 585 TobiasBg 586 Todd Berman 587 Tomas-A 588 Tomas Varaneckas 589 Tom Erik Støwer 590 Tom Klancer 591 Tom MacWright 592 Tony Jian 593 Travis Heppe 594 Triangle717 595 Tristan Tarrant 596 TSUYUSATO Kitsune 597 twifkak 598 VapidWorx 599 Vestimir Markov 600 vf 601 Victor Bocharsky 602 Vincent Woo 603 Volker Mische 604 Weiyan Shao 605 wenli 606 Wes Cossick 607 Wesley Wiser 608 Will Binns-Smith 609 Will Dean 610 William Jamieson 611 William Stein 612 Willy 613 Wojtek Ptak 614 Wu Cheng-Han 615 Xavier Mendez 616 Yassin N. Hassan 617 YNH Webdev 618 Yunchi Luo 619 Yuvi Panda 620 Zac Anger 621 Zachary Dremann 622 Zeno Rocha 623 Zhang Hao 624 zziuni 625 魏鹏刚 -
src/wp-includes/js/codemirror/CHANGELOG.md
1 ## 5.23.0 (2017-01-19) 2 3 ### Bug fixes 4 5 Presentation-related elements DOM elements are now marked as such to help screen readers. 6 7 [markdown mode](http://codemirror.net/mode/markdown/): Be more picky about what HTML tags look like to avoid false positives. 8 9 ### New features 10 11 `findModeByMIME` now understands `+json` and `+xml` MIME suffixes. 12 13 [closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets): Add support for an `override` option to ignore language-specific defaults. 14 15 [panel addon](http://codemirror.net/doc/manual.html#addon_panel): Add a `stable` option that auto-scrolls the content to keep it in the same place when inserting/removing a panel. 16 17 ## 5.22.2 (2017-01-12) 18 19 ### Bug fixes 20 21 Include rollup.config.js in NPM package, so that it can be used to build from source. 22 23 ## 5.22.0 (2016-12-20) 24 25 ### Bug fixes 26 27 [sublime bindings](http://codemirror.net/demo/sublime.html): Make `selectBetweenBrackets` work with multiple cursors. 28 29 [javascript mode](http://codemirror.net/mode/javascript/): Fix issues with parsing complex TypeScript types, imports, and exports. 30 31 A contentEditable editor instance with autofocus enabled no longer crashes during initializing. 32 33 ### New features 34 35 [emacs bindings](http://codemirror.net/demo/emacs.html): Export `CodeMirror.emacs` to allow other addons to hook into Emacs-style functionality. 36 37 [active-line addon](http://codemirror.net/doc/manual.html#addon_active-line): Add `nonEmpty` option. 38 39 New event: [`optionChange`](http://codemirror.net/doc/manual.html#event_optionChange). 40 41 ## 5.21.0 (2016-11-21) 42 43 ### Bug fixes 44 45 Tapping/clicking the editor in [contentEditable mode](http://codemirror.net/doc/manual.html#option_inputStyle) on Chrome now puts the cursor at the tapped position. 46 47 Fix various crashes and misbehaviors when reading composition events in [contentEditable mode](http://codemirror.net/doc/manual.html#option_inputStyle). 48 49 Catches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a `<body>`. 50 51 [merge addon](http://codemirror.net/doc/manual.html#addon_merge): Fix several issues in the chunk-aligning feature. 52 53 [verilog mode](http://codemirror.net/mode/verilog): Rewritten to address various issues. 54 55 [julia mode](http://codemirror.net/mode/julia): Recognize Julia 0.5 syntax. 56 57 [swift mode](http://codemirror.net/mode/swift): Various fixes and adjustments to current syntax. 58 59 [markdown mode](http://codemirror.net/mode/markdown): Allow lists without a blank line above them. 60 61 ### New features 62 63 The [`setGutterMarker`](http://codemirror.net/doc/manual.html#setGutterMarker), [`clearGutter`](http://codemirror.net/doc/manual.html#clearGutter), and [`lineInfo`](http://codemirror.net/doc/manual.html#lineInfo) methods are now available on `Doc` objects. 64 65 The [`heightAtLine`](http://codemirror.net/doc/manual.html#heightAtLine) method now takes an extra argument to allow finding the height at the top of the line's line widgets. 66 67 [ruby mode](http://codemirror.net/mode/ruby): `else` and `elsif` are now immediately indented. 68 69 [vim bindings](http://codemirror.net/demo/vim.html): Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode. 70 71 ## 5.20.2 (2016-10-21) 72 73 ### Bug fixes 74 75 Fix `CodeMirror.version` returning the wrong version number. 76 77 ## 5.20.0 (2016-10-20) 78 79 ### Bug fixes 80 81 Make `newlineAndIndent` command work with multiple cursors on the same line. 82 83 Make sure keypress events for backspace are ignored. 84 85 Tokens styled with overlays no longer get a nonsense `cm-cm-overlay` class. 86 87 Line endings for pasted content are now normalized to the editor's [preferred ending](http://codemirror.net/doc/manual.html#option_lineSeparator). 88 89 [javascript mode](http://codemirror.net/mode/javascript): Improve support for class expressions. Support TypeScript optional class properties, the `abstract` keyword, and return type declarations for arrow functions. 90 91 [css mode](http://codemirror.net/mode/css): Fix highlighting of mixed-case keywords. 92 93 [closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets): Improve behavior when typing a quote before a string. 94 95 ### New features 96 97 The core is now maintained as a number of small files, using ES6 syntax and modules, under the `src/` directory. A git checkout no longer contains a working `codemirror.js` until you `npm build` (but when installing from NPM, it is included). 98 99 The [`refresh`](http://codemirror.net/doc/manual.html#event_refresh) event is now documented and stable. 100 101 ## 5.19.0 (2016-09-20) 102 103 ### Bugfixes 104 105 [erlang mode](http://codemirror.net/mode/erlang): Fix mode crash when trying to read an empty context. 106 107 [comment addon](http://codemirror.net/doc/manual.html#addon_comment): Fix broken behavior when toggling comments inside a comment. 108 109 xml-fold addon: Fix a null-dereference bug. 110 111 Page up and page down now do something even in single-line documents. 112 113 Fix an issue where the cursor position could be off in really long (~8000 character) tokens. 114 115 ### New features 116 117 [javascript mode](http://codemirror.net/mode/javascript): Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the `type` keyword. 118 119 The [`blur`](http://codemirror.net/doc/manual.html#event_blur) and [`focus`](http://codemirror.net/doc/manual.html#event_focus) events now pass the DOM event to their handlers. 120 121 ## 5.18.2 (2016-08-23) 122 123 ### Bugfixes 124 125 [vue mode](http://codemirror.net/mode/vue): Fix outdated references to renamed Pug mode dependency. 126 127 ## 5.18.0 (2016-08-22) 128 129 ### Bugfixes 130 131 Make sure [gutter backgrounds](http://codemirror.net/doc/manual.html#addLineClass) stick to the rest of the gutter during horizontal scrolling. 132 133 The contenteditable [`inputStyle`](http://codemirror.net/doc/manual.html#option_inputStyle) now properly supports pasting on pre-Edge IE versions. 134 135 [javascript mode](http://codemirror.net/mode/javascript): Fix some small parsing bugs and improve TypeScript support. 136 137 [matchbrackets addon](http://codemirror.net/doc/manual.html#addon_matchbrackets): Fix bug where active highlighting was left in editor when the addon was disabled. 138 139 [match-highlighter addon](http://codemirror.net/doc/manual.html#addon_match-highlighter): Only start highlighting things when the editor gains focus. 140 141 [javascript-hint addon](http://codemirror.net/doc/manual.html#addon_javascript-hint): Also complete non-enumerable properties. 142 143 ### New features 144 145 The [`addOverlay`](http://codemirror.net/doc/manual.html#addOverlay) method now supports a `priority` option to control the order in which overlays are applied. 146 147 MIME types that end in `+json` now default to the JSON mode when the MIME itself is not defined. 148 149 ### Breaking changes 150 151 The mode formerly known as Jade was renamed to [Pug](http://codemirror.net/mode/pug). 152 153 The [Python mode](http://codemirror.net/mode/python) now defaults to Python 3 (rather than 2) syntax. 154 155 ## 5.17.0 (2016-07-19) 156 157 ### Bugfixes 158 159 Fix problem with wrapped trailing whitespace displaying incorrectly. 160 161 Prevent IME dialog from overlapping typed content in Chrome. 162 163 Improve measuring of characters near a line wrap. 164 165 [javascript mode](http://codemirror.net/mode/javascript): Improve support for `async`, allow trailing commas in `import` lists. 166 167 [vim bindings](http://codemirror.net/demo/vim.html): Fix backspace in replace mode. 168 169 [sublime bindings](http://codemirror.net/demo/sublime.html): Fix some key bindings on OS X to match Sublime Text. 170 171 ### New features 172 173 [markdown mode](http://codemirror.net/mode/markdown): Add more classes to image links in highlight-formatting mode. 174 175 ## 5.16.0 (2016-06-20) 176 177 ### Bugfixes 178 179 Fix glitches when dragging content caused by the drop indicator receiving mouse events. 180 181 Make Control-drag work on Firefox. 182 183 Make clicking or selection-dragging at the end of a wrapped line select the right position. 184 185 [show-hint addon](http://codemirror.net/doc/manual.html#addon_show-hint): Prevent widget scrollbar from hiding part of the hint text. 186 187 [rulers addon](http://codemirror.net/doc/manual.html#addon_rulers): Prevent rulers from forcing a horizontal editor scrollbar. 188 189 ### New features 190 191 [search addon](http://codemirror.net/doc/manual.html#addon_search): Automatically bind search-related keys in persistent dialog. 192 193 [sublime keymap](http://codemirror.net/demo/sublime.html): Add a multi-cursor aware smart backspace binding. 194 195 ## 5.15.2 (2016-05-20) 196 197 ### Bugfixes 198 199 Fix a critical document corruption bug that occurs when a document is gradually grown. 200 201 ## 5.15.0 (2016-05-20) 202 203 ### Bugfixes 204 205 Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode. 206 207 Fix issue where not all ASCII control characters were being replaced by placeholders. 208 209 Remove the assumption that all modes have a `startState` method from several wrapping modes. 210 211 Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any. 212 213 Optimize document tree building when loading or pasting huge chunks of content. 214 215 [markdown mode](http://codemirror.net/mode/markdown/): Fix several issues in matching link targets. 216 217 [clike mode](http://codemirror.net/mode/clike/): Improve indentation of C++ template declarations. 218 219 ### New features 220 221 Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected. 222 223 Pasting [linewise-copied](http://codemirror.net/doc/manual.html#option_lineWiseCopyCut) content when there is no selection now inserts the lines above the current line. 224 225 [javascript mode](http://codemirror.net/mode/javascript/): Support `async`/`await` and improve support for TypeScript type syntax. 226 227 ## 5.14.2 (2016-04-20) 228 229 ### Bugfixes 230 231 Push a new package to NPM due to an [NPM bug](https://github.com/npm/npm/issues/5082) omitting the LICENSE file in 5.14.0. 232 233 Set `dataTransfer.effectAllowed` in `dragstart` handler to help browsers use the right drag icon. 234 235 Add the [mbox mode](http://codemirror.net/mode/mbox/index.html) to `mode/meta.js`. 236 237 ## 5.14.0 (2016-04-20) 238 239 ### Bugfixes 240 241 [`posFromIndex`](http://codemirror.net/doc/manual.html#posFromIndex) and [`indexFromPos`](http://codemirror.net/doc/manual.html#indexFromPos) now take [`lineSeparator`](http://codemirror.net/doc/manual.html#option_lineSeparator) into account. 242 243 [vim bindings](http://codemirror.net/demo/vim.html): Only call `.save()` when it is actually available. 244 245 [comment addon](http://codemirror.net/doc/manual.html#addon_comment): Be careful not to mangle multi-line strings. 246 247 [Python mode](http://codemirror.net/mode/python/index.html): Improve distinguishing of decorators from `@` operators. 248 249 [`findMarks`](http://codemirror.net/doc/manual.html#findMarks): No longer return marks that touch but don't overlap given range. 250 251 ### New features 252 253 [vim bindings](http://codemirror.net/demo/vim.html): Add yank command. 254 255 [match-highlighter addon](http://codemirror.net/doc/manual.html#addon_match-highlighter): Add `trim` option to disable ignoring of whitespace. 256 257 [PowerShell mode](http://codemirror.net/mode/powershell/index.html): Added. 258 259 [Yacas mode](http://codemirror.net/mode/yacas/index.html): Added. 260 261 [Web IDL mode](http://codemirror.net/mode/webidl/index.html): Added. 262 263 [SAS mode](http://codemirror.net/mode/sas/index.html): Added. 264 265 [mbox mode](http://codemirror.net/mode/mbox/index.html): Added. 266 267 ## 5.13.2 (2016-03-23) 268 269 ### Bugfixes 270 271 Solves a problem where the gutter would sometimes not extend all the way to the end of the document. 272 273 ## 5.13.0 (2016-03-21) 274 275 ### New features 276 277 New DOM event forwarded: [`"dragleave"`](http://codemirror.net/doc/manual.html#event_dom). 278 279 [protobuf mode](http://codemirror.net/mode/protobuf/index.html): Newly added. 280 281 ### Bugfixes 282 283 Fix problem where [`findMarks`](http://codemirror.net/doc/manual.html#findMarks) sometimes failed to find multi-line marks. 284 285 Fix crash that showed up when atomic ranges and bidi text were combined. 286 287 [show-hint addon](http://codemirror.net/demo/complete.html): Completion widgets no longer close when the line indented or dedented. 288 289 [merge addon](http://codemirror.net/demo/merge.html): Fix bug when merging chunks at the end of the file. 290 291 [placeholder addon](http://codemirror.net/doc/manual.html#addon_placeholder): No longer gets confused by [`swapDoc`](http://codemirror.net/doc/manual.html#swapDoc). 292 293 [simplescrollbars addon](http://codemirror.net/doc/manual.html#addon_simplescrollbars): Fix invalid state when deleting at end of document. 294 295 [clike mode](http://codemirror.net/mode/clike/index.html): No longer gets confused when a comment starts after an operator. 296 297 [markdown mode](http://codemirror.net/mode/markdown/index.html): Now supports CommonMark-style flexible list indentation. 298 299 [dylan mode](http://codemirror.net/mode/dylan/index.html): Several improvements and fixes. 300 301 ## 5.12.0 (2016-02-19) 302 303 ### New features 304 305 [Vim bindings](http://codemirror.net/demo/vim.html): Ctrl-Q is now an alias for Ctrl-V. 306 307 [Vim bindings](http://codemirror.net/demo/vim.html): The Vim API now exposes an `unmap` method to unmap bindings. 308 309 [active-line addon](http://codemirror.net/demo/activeline.html): This addon can now style the active line's gutter. 310 311 [FCL mode](http://codemirror.net/mode/fcl/): Newly added. 312 313 [SQL mode](http://codemirror.net/mode/sql/): Now has a Postgresql dialect. 314 315 ### Bugfixes 316 317 Fix [issue](https://github.com/codemirror/CodeMirror/issues/3781) where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly. 318 319 Use absolute, rather than fixed positioning in the context-menu intercept hack, to work around a [problem](https://github.com/codemirror/CodeMirror/issues/3238) when the editor is inside a transformed parent container. 320 321 Solve a [problem](https://github.com/codemirror/CodeMirror/issues/3821) where the horizontal scrollbar could hide text in Firefox. 322 323 Fix a [bug](https://github.com/codemirror/CodeMirror/issues/3834) that caused phantom scroll space under the text in some situations. 324 325 [Sublime Text bindings](http://codemirror.net/demo/sublime.html): Bind delete-line to Shift-Ctrl-K on OS X. 326 327 [Markdown mode](http://codemirror.net/mode/markdown/): Fix [issue](https://github.com/codemirror/CodeMirror/issues/3787) where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses. 328 329 [Markdown mode](http://codemirror.net/mode/markdown/): Ignore backslashes in code fragments. 330 331 [Markdown mode](http://codemirror.net/mode/markdown/): Use whichever mode is registered as `text/html` to parse HTML. 332 333 [Clike mode](http://codemirror.net/mode/clike/): Improve indentation of Scala `=>` functions. 334 335 [Python mode](http://codemirror.net/mode/python/): Improve indentation of bracketed code. 336 337 [HTMLMixed mode](http://codemirror.net/mode/htmlmixed/): Support multi-line opening tags for sub-languages (`<script>`, `<style>`, etc). 338 339 [Spreadsheet mode](http://codemirror.net/mode/spreadsheet/): Fix bug where the mode did not advance the stream when finding a backslash. 340 341 [XML mode](http://codemirror.net/mode/xml/): The mode now takes a `matchClosing` option to configure whether mismatched closing tags should be highlighted as errors. 342 343 ## 5.11.0 (2016-01-20) 344 345 * New modes: [JSX](http://codemirror.net/mode/jsx/index.html), [literate Haskell](http://codemirror.net/mode/haskell-literate/index.html) 346 * The editor now forwards more [DOM events](http://codemirror.net/doc/manual.html#event_dom): `cut`, `copy`, `paste`, and `touchstart`. It will also forward `mousedown` for drag events 347 * Fixes a bug where bookmarks next to collapsed spans were not rendered 348 * The [Swift](http://codemirror.net/mode/swift/index.html) mode now supports auto-indentation 349 * Frontmatters in the [YAML frontmatter](http://codemirror.net/mode/yaml-frontmatter/index.html) mode are now optional as intended 350 351 ## 5.10.0 (2015-12-21) 352 353 * Modify the way [atomic ranges](http://codemirror.net/doc/manual.html#mark_atomic) are skipped by selection to try and make it less surprising. 354 * The [Swift mode](http://codemirror.net/mode/swift/index.html) was rewritten. 355 * New addon: [jump-to-line](http://codemirror.net/doc/manual.html#addon_jump-to-line). 356 * New method: [`isReadOnly`](http://codemirror.net/doc/manual.html#isReadOnly). 357 * The [show-hint addon](http://codemirror.net/doc/manual.html#addon_show-hint) now defaults to picking completions on single click. 358 * The object passed to [`"beforeSelectionChange"`](http://codemirror.net/doc/manual.html#event_beforeSelectionChange) events now has an `origin` property. 359 * New mode: [Crystal](http://codemirror.net/mode/crystal/index.html). 360 361 ## 5.9.0 (2015-11-23) 362 363 * Improve the way overlay (OS X-style) scrollbars are handled 364 * Make [annotatescrollbar](http://codemirror.net/doc/manual.html#addon_annotatescrollbar) and scrollpastend addons work properly together 365 * Make [show-hint](http://codemirror.net/doc/manual.html#addon_show-hint) addon select options on single click by default, move selection to hovered item 366 * Properly fold comments that include block-comment-start markers 367 * Many small language mode fixes 368 369 ## 5.8.0 (2015-10-20) 370 371 * Fixes an infinite loop in the [hardwrap addon](http://codemirror.net/doc/manual.html#addon_hardwrap) 372 * New modes: [NSIS](http://codemirror.net/mode/nsis/index.html), [Ceylon](http://codemirror.net/mode/clike/index.html) 373 * The Kotlin mode is now a [clike](http://codemirror.net/mode/clike/index.html) dialect, rather than a stand-alone mode 374 * New option: [`allowDropFileTypes`](http://codemirror.net/doc/manual.html#option_allowDropFileTypes). Binary files can no longer be dropped into CodeMirror 375 * New themes: [bespin](http://codemirror.net/demo/theme.html#bespin), [hopscotch](http://codemirror.net/demo/theme.html#hopscotch), [isotope](http://codemirror.net/demo/theme.html#isotope), [railscasts](http://codemirror.net/demo/theme.html#railscasts) 376 377 ## 5.7.0 (2015-09-21) 378 379 * New modes: [Vue](http://codemirror.net/mode/vue/index.html), [Oz](http://codemirror.net/mode/oz/index.html), [MscGen](http://codemirror.net/mode/mscgen/index.html) (and dialects), [Closure Stylesheets](http://codemirror.net/mode/css/gss.html) 380 * Implement [CommonMark](http://commonmark.org)-style flexible list indent and cross-line code spans in [Markdown](http://codemirror.net/mode/markdown/index.html) mode 381 * Add a replace-all button to the [search addon](http://codemirror.net/doc/manual.html#addon_search), and make the persistent search dialog transparent when it obscures the match 382 * Handle `acync`/`await` and ocal and binary numbers in [JavaScript mode](http://codemirror.net/mode/javascript/index.html) 383 * Fix various issues with the [Haxe mode](http://codemirror.net/mode/haxe/index.html) 384 * Make the [closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets) select only the wrapped text when wrapping selection in brackets 385 * Tokenize properties as properties in the [CoffeeScript mode](http://codemirror.net/mode/coffeescript/index.html) 386 * The [placeholder addon](http://codemirror.net/doc/manual.html#addon_placeholder) now accepts a DOM node as well as a string placeholder 387 388 ## 5.6.0 (2015-08-20) 389 390 * Fix bug where you could paste into a `readOnly` editor 391 * Show a cursor at the drop location when dragging over the editor 392 * The [Rust mode](http://codemirror.net/mode/rust/index.html) was rewritten to handle modern Rust 393 * The editor and theme CSS was cleaned up. Some selectors are now less specific than before 394 * New theme: [abcdef](http://codemirror.net/demo/theme.html#abcdef) 395 * Lines longer than [`maxHighlightLength`](http://codemirror.net/doc/manual.html#option_maxHighlightLength) are now less likely to mess up indentation 396 * New addons: [`autorefresh`](http://codemirror.net/doc/manual.html#addon_autorefresh) for refreshing an editor the first time it becomes visible, and `html-lint` for using [HTMLHint](http://htmlhint.com/) 397 * The [`search`](http://codemirror.net/doc/manual.html#addon_search) addon now recognizes `\r` and `\n` in pattern and replacement input 398 399 ## 5.5.0 (2015-07-20) 400 401 * New option: [`lineSeparator`](http://codemirror.net/doc/manual.html#option_lineSeparator) (with corresponding [method](http://codemirror.net/doc/manual.html#lineSeparator)) 402 * New themes: [dracula](http://codemirror.net/demo/theme.html#dracula), [seti](http://codemirror.net/demo/theme.html#seti), [yeti](http://codemirror.net/demo/theme.html#yeti), [material](http://codemirror.net/demo/theme.html#material), and [icecoder](http://codemirror.net/demo/theme.html#icecoder) 403 * New modes: [Brainfuck](http://codemirror.net/mode/brainfuck/index.html), [VHDL](http://codemirror.net/mode/vhdl/index.html), Squirrel ([clike](http://codemirror.net/mode/clike/index.html) dialect) 404 * Define a `findPersistent` command in the [search](http://codemirror.net/demo/search.html) addon, for a dialog that stays open as you cycle through matches 405 * From this release on, the NPM module doesn't include documentation and demos 406 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.4.0...5.5.0) 407 408 ## 5.4.0 (2015-06-25) 409 410 * New modes: [Twig](http://codemirror.net/mode/twig/index.html), [Elm](http://codemirror.net/mode/elm/index.html), [Factor](http://codemirror.net/mode/factor/index.html), [Swift](http://codemirror.net/mode/swift/index.html) 411 * Prefer clipboard API (if available) when pasting 412 * Refined definition highlighting in [clike](http://codemirror.net/mode/clike/index.html) mode 413 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.3.0...5.4.0) 414 415 ## 5.3.0 (2015-05-20) 416 417 * Fix several regressions in the [`show-hint`](http://codemirror.net/doc/manual.html#addon_show-hint) addon (`completeSingle` option, `"shown"` and `"close"` events) 418 * The [vim mode](http://codemirror.net/demo/vim.html) API was [documented](http://codemirror.net/doc/manual.html#vimapi) 419 * New modes: [ASN.1](http://codemirror.net/mode/asn.1/index.html), [TTCN](http://codemirror.net/mode/ttcn/index.html), and [TTCN-CFG](http://codemirror.net/mode/ttcn-cfg/index.html) 420 * The [clike](http://codemirror.net/mode/clike/index.html) mode can now deep-indent `switch` statements, and roughly recognizes types and defined identifiers 421 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.2.0...5.3.0) 422 423 ## 5.2.0 (2015-04-20) 424 425 * Fix several race conditions in [`show-hint`](http://codemirror.net/doc/manual.html#addon_show-hint)'s asynchronous mode 426 * Fix backspace binding in [Sublime bindings](http://codemirror.net/demo/sublime.html) 427 * Change the way IME is handled in the `"textarea"` [input style](http://codemirror.net/doc/manual.html#option_inputStyle) 428 * New modes: [MUMPS](http://codemirror.net/mode/mumps/index.html), [Handlebars](http://codemirror.net/mode/handlebars/index.html) 429 * Rewritten modes: [Django](http://codemirror.net/mode/django/index.html), [Z80](http://codemirror.net/mode/z80/index.html) 430 * New theme: [Liquibyte](http://codemirror.net/demo/theme.html#liquibyte) 431 * New option: [`lineWiseCopyCut`](http://codemirror.net/doc/manual.html#option_lineWiseCopyCut) 432 * The [Vim mode](http://codemirror.net/demo/vim.html) now supports buffer-local options and the `filetype` setting 433 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.1.0...5.2.0) 434 435 ## 5.1.0 (2015-03-23) 436 437 * New modes: [ASCII armor](http://codemirror.net/mode/asciiarmor/index.html) (PGP data), [Troff](http://codemirror.net/mode/troff/index.html), and [CMake](http://codemirror.net/mode/cmake/index.html). 438 * Remove SmartyMixed mode, rewrite [Smarty](http://codemirror.net/mode/smarty/index.html) mode to supersede it. 439 * New commands in the [merge addon](http://codemirror.net/doc/manual.html#addon_merge): `goNextDiff` and `goPrevDiff`. 440 * The [closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets) can now be configured per mode. 441 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/5.0.0...5.1.0). 442 443 ## 5.0.0 (2015-02-20) 444 445 * Experimental mobile support (tested on iOS, Android Chrome, stock Android browser) 446 * New option [`inputStyle`](http://codemirror.net/doc/manual.html#option_inputStyle) to switch between hidden textarea and contenteditable input. 447 * The [`getInputField`](http://codemirror.net/doc/manual.html#getInputField) method is no longer guaranteed to return a textarea. 448 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.13.0...5.0.0). 449 450 ## 4.13.0 (2015-02-20) 451 452 * Fix the way the [`closetag`](http://codemirror.net/demo/closetag.html) demo handles the slash character. 453 * New modes: [Forth](http://codemirror.net/mode/forth/index.html), [Stylus](http://codemirror.net/mode/stylus/index.html). 454 * Make the [CSS mode](http://codemirror.net/mode/css/index.html) understand some modern CSS extensions. 455 * Have the [Scala mode](http://codemirror.net/mode/clike/index.html) handle symbols and triple-quoted strings. 456 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.12.0...4.13.0). 457 458 ## 4.12.0 (2015-01-22) 459 460 * The [`closetag`](http://codemirror.net/doc/manual.html#addon_closetag) addon now defines a `"closeTag"` command. 461 * Adds a `findModeByFileName` to the [mode metadata](http://codemirror.net/doc/manual.html#addon_meta) addon. 462 * [Simple mode](http://codemirror.net/demo/simplemode.html) rules can now contain a `sol` property to only match at the start of a line. 463 * New addon: [`selection-pointer`](http://codemirror.net/doc/manual.html#addon_selection-pointer) to style the mouse cursor over the selection. 464 * Improvements to the [Sass mode](http://codemirror.net/mode/sass/index.html)'s indentation. 465 * The [Vim keymap](http://codemirror.net/demo/vim.html)'s search functionality now supports [scrollbar annotation](http://codemirror.net/doc/manual.html#addon_matchesonscrollbar). 466 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.11.0...4.12.0). 467 468 ## 4.11.0 (2015-01-09) 469 470 Unfortunately, 4.10 did not take care of the Firefox scrolling issue entirely. This release adds two more patches to address that. 471 472 ## 4.10.0 (2014-12-29) 473 474 Emergency single-patch update to 4.9\. Fixes Firefox-specific problem where the cursor could end up behind the horizontal scrollbar. 475 476 ## 4.9.0 (2014-12-23) 477 478 * Overhauled scroll bar handling. Add pluggable [scrollbar implementations](http://codemirror.net/demo/simplescrollbars.html). 479 * Tweaked behavior for the [completion addons](http://codemirror.net/doc/manual.html#addon_show-hint) to not take text after cursor into account. 480 * Two new optional features in the [merge addon](http://codemirror.net/doc/manual.html#addon_merge): aligning editors, and folding unchanged text. 481 * New modes: [Dart](http://codemirror.net/mode/dart/index.html), [EBNF](http://codemirror.net/mode/ebnf/index.html), [spreadsheet](http://codemirror.net/mode/spreadsheet/index.html), and [Soy](http://codemirror.net/mode/soy/index.html). 482 * New [addon](http://codemirror.net/demo/panel.html) to show persistent panels below/above an editor. 483 * New themes: [zenburn](http://codemirror.net/demo/theme.html#zenburn) and [tomorrow night bright](http://codemirror.net/demo/theme.html#tomorrow-night-bright). 484 * Allow ctrl-click to clear existing cursors. 485 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.8.0...4.9.0). 486 487 ## 4.8.0 (2014-11-22) 488 489 * Built-in support for [multi-stroke key bindings](http://codemirror.net/doc/manual.html#normalizeKeyMap). 490 * New method: [`getLineTokens`](http://codemirror.net/doc/manual.html#getLineTokens). 491 * New modes: [dockerfile](http://codemirror.net/mode/dockerfile/index.html), [IDL](http://codemirror.net/mode/idl/index.html), [Objective C](http://codemirror.net/mode/clike/index.html) (crude). 492 * Support styling of gutter backgrounds, allow `"gutter"` styles in [`addLineClass`](http://codemirror.net/doc/manual.html#addLineClass). 493 * Many improvements to the [Vim mode](http://codemirror.net/demo/vim.html), rewritten visual mode. 494 * Improvements to modes: [gfm](http://codemirror.net/mode/gfm/index.html) (strikethrough), [SPARQL](http://codemirror.net/mode/sparql/index.html) (version 1.1 support), and [sTeX](http://codemirror.net/mode/stex/index.html) (no more runaway math mode). 495 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.7.0...4.8.0). 496 497 ## 4.7.0 (2014-10-20) 498 499 * **Incompatible**: The [lint addon](http://codemirror.net/demo/lint.html) now passes the editor's value as first argument to asynchronous lint functions, for consistency. The editor is still passed, as fourth argument. 500 * Improved handling of unicode identifiers in modes for languages that support them. 501 * More mode improvements: [CoffeeScript](http://codemirror.net/mode/coffeescript/index.html) (indentation), [Verilog](http://codemirror.net/mode/verilog/index.html) (indentation), [Scala](http://codemirror.net/mode/clike/index.html) (indentation, triple-quoted strings), and [PHP](http://codemirror.net/mode/php/index.html) (interpolated variables in heredoc strings). 502 * New modes: [Textile](http://codemirror.net/mode/textile/index.html) and [Tornado templates](http://codemirror.net/mode/tornado/index.html). 503 * Experimental new [way to define modes](http://codemirror.net/demo/simplemode.html). 504 * Improvements to the [Vim bindings](http://codemirror.net/demo/vim.html): Arbitrary insert mode key mappings are now possible, and text objects are supported in visual mode. 505 * The mode [meta-information file](http://codemirror.net/mode/meta.js) now includes information about file extensions, and [helper functions](http://codemirror.net/doc/manual.html#addon_meta) `findModeByMIME` and `findModeByExtension`. 506 * New logo! 507 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.6.0...4.7.0). 508 509 ## 4.6.0 (2014-09-19) 510 511 * New mode: [Modelica](http://codemirror.net/mode/modelica/index.html) 512 * New method: [`findWordAt`](http://codemirror.net/doc/manual.html#findWordAt) 513 * Make it easier to [use text background styling](http://codemirror.net/demo/markselection.html) 514 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.5.0...4.6.0). 515 516 ## 4.5.0 (2014-08-21) 517 518 * Fix several serious bugs with horizontal scrolling 519 * New mode: [Slim](http://codemirror.net/mode/slim/index.html) 520 * New command: [`goLineLeftSmart`](http://codemirror.net/doc/manual.html#command_goLineLeftSmart) 521 * More fixes and extensions for the [Vim](http://codemirror.net/demo/vim.html) visual block mode 522 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.4.0...4.5.0). 523 524 ## 4.4.0 (2014-07-21) 525 526 * **Note:** Some events might now fire in slightly different order (`"change"` is still guaranteed to fire before `"cursorActivity"`) 527 * Nested operations in multiple editors are now synced (complete at same time, reducing DOM reflows) 528 * Visual block mode for [vim](http://codemirror.net/demo/vim.html) (<C-v>) is nearly complete 529 * New mode: [Kotlin](http://codemirror.net/mode/kotlin/index.html) 530 * Better multi-selection paste for text copied from multiple CodeMirror selections 531 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.3.0...4.4.0). 532 533 ## 4.3.0 (2014-06-23) 534 535 * Several [vim bindings](http://codemirror.net/demo/vim.html) improvements: search and exCommand history, global flag for `:substitute`, `:global` command. 536 * Allow hiding the cursor by setting [`cursorBlinkRate`](http://codemirror.net/doc/manual.html#option_cursorBlinkRate) to a negative value. 537 * Make gutter markers themeable, use this in foldgutter. 538 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.2.0...4.3.0). 539 540 ## 4.2.0 (2014-05-19) 541 542 * Fix problem where some modes were broken by the fact that empty tokens were forbidden. 543 * Several fixes to context menu handling. 544 * On undo, scroll _change_, not cursor, into view. 545 * Rewritten [Jade](http://codemirror.net/mode/jade/index.html) mode. 546 * Various improvements to [Shell](http://codemirror.net/mode/shell/index.html) (support for more syntax) and [Python](http://codemirror.net/mode/python/index.html) (better indentation) modes. 547 * New mode: [Cypher](http://codemirror.net/mode/cypher/index.html). 548 * New theme: [Neo](http://codemirror.net/demo/theme.html#neo). 549 * Support direct styling options (color, line style, width) in the [rulers](http://codemirror.net/doc/manual.html#addon_rulers) addon. 550 * Recognize per-editor configuration for the [show-hint](http://codemirror.net/doc/manual.html#addon_show-hint) and [foldcode](http://codemirror.net/doc/manual.html#addon_foldcode) addons. 551 * More intelligent scanning for existing close tags in [closetag](http://codemirror.net/doc/manual.html#addon_closetag) addon. 552 * In the [Vim bindings](http://codemirror.net/demo/vim.html): Fix bracket matching, support case conversion in visual mode, visual paste, append action. 553 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.1.0...4.2.0). 554 555 ## 4.1.0 (2014-04-22) 556 557 * _Slightly incompatible_: The [`"cursorActivity"`](http://codemirror.net/doc/manual.html#event_cursorActivity) event now fires after all other events for the operation (and only for handlers that were actually registered at the time the activity happened). 558 * New command: [`insertSoftTab`](http://codemirror.net/doc/manual.html#command_insertSoftTab). 559 * New mode: [Django](http://codemirror.net/mode/django/index.html). 560 * Improved modes: [Verilog](http://codemirror.net/mode/verilog/index.html) (rewritten), [Jinja2](http://codemirror.net/mode/jinja2/index.html), [Haxe](http://codemirror.net/mode/haxe/index.html), [PHP](http://codemirror.net/mode/php/index.html) (string interpolation highlighted), [JavaScript](http://codemirror.net/mode/javascript/index.html) (indentation of trailing else, template strings), [LiveScript](http://codemirror.net/mode/livescript/index.html) (multi-line strings). 561 * Many small issues from the 3.x→4.x transition were found and fixed. 562 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/4.0.3...4.1.0). 563 564 ## 3.24.0 (2014-04-22) 565 566 Merges the improvements from 4.1 that could easily be applied to the 3.x code. Also improves the way the editor size is updated when line widgets change. 567 568 ## 3.23.0 (2014-03-20) 569 570 * In the [XML mode](http://codemirror.net/mode/xml/index.html), add `brackets` style to angle brackets, fix case-sensitivity of tags for HTML. 571 * New mode: [Dylan](http://codemirror.net/mode/dylan/index.html). 572 * Many improvements to the [Vim bindings](http://codemirror.net/demo/vim.html). 573 574 ## 3.22.0 (2014-02-21) 575 576 * Adds the [`findMarks`](http://codemirror.net/doc/manual.html#findMarks) method. 577 * New addons: [rulers](http://codemirror.net/doc/manual.html#addon_rulers), markdown-fold, yaml-lint. 578 * New theme: [mdn-like](http://codemirror.net/demo/theme.html#mdn-like). 579 * New mode: [Solr](http://codemirror.net/mode/solr/index.html). 580 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.21.0...3.22.0). 581 582 ## 3.21.0 (2014-01-16) 583 584 * Auto-indenting a block will no longer add trailing whitespace to blank lines. 585 * Marking text has a new option [`clearWhenEmpty`](http://codemirror.net/doc/manual.html#markText) to control auto-removal. 586 * Several bugfixes in the handling of bidirectional text. 587 * The [XML](http://codemirror.net/mode/xml/index.html) and [CSS](http://codemirror.net/mode/css/index.html) modes were largely rewritten. [LESS](http://codemirror.net/mode/css/less.html) support was added to the CSS mode. 588 * The OCaml mode was moved to an [mllike](http://codemirror.net/mode/mllike/index.html) mode, F# support added. 589 * Make it possible to fetch multiple applicable helper values with [`getHelpers`](http://codemirror.net/doc/manual.html#getHelpers), and to register helpers matched on predicates with [`registerGlobalHelper`](http://codemirror.net/doc/manual.html#registerGlobalHelper). 590 * New theme [pastel-on-dark](http://codemirror.net/demo/theme.html#pastel-on-dark). 591 * Better ECMAScript 6 support in [JavaScript](http://codemirror.net/mode/javascript/index.html) mode. 592 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.20.0...3.21.0). 593 594 ## 3.20.0 (2013-11-21) 595 596 * New modes: [Julia](http://codemirror.net/mode/julia/index.html) and [PEG.js](http://codemirror.net/mode/pegjs/index.html). 597 * Support ECMAScript 6 in the [JavaScript mode](http://codemirror.net/mode/javascript/index.html). 598 * Improved indentation for the [CoffeeScript mode](http://codemirror.net/mode/coffeescript/index.html). 599 * Make non-printable-character representation [configurable](http://codemirror.net/doc/manual.html#option_specialChars). 600 * Add ‘notification’ functionality to [dialog](http://codemirror.net/doc/manual.html#addon_dialog) addon. 601 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.19.0...3.20.0). 602 603 ## 3.19.0 (2013-10-21) 604 605 * New modes: [Eiffel](http://codemirror.net/mode/eiffel/index.html), [Gherkin](http://codemirror.net/mode/gherkin/index.html), [MSSQL dialect](http://codemirror.net/mode/sql/?mime=text/x-mssql). 606 * New addons: [hardwrap](http://codemirror.net/doc/manual.html#addon_hardwrap), [sql-hint](http://codemirror.net/doc/manual.html#addon_sql-hint). 607 * New theme: [MBO](http://codemirror.net/demo/theme.html#mbo). 608 * Add [support](http://codemirror.net/doc/manual.html#token_style_line) for line-level styling from mode tokenizers. 609 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.18.0...3.19.0). 610 611 ## 3.18.0 (2013-09-23) 612 613 Emergency release to fix a problem in 3.17 where `.setOption("lineNumbers", false)` would raise an error. 614 615 ## 3.17.0 (2013-09-23) 616 617 * New modes: [Fortran](http://codemirror.net/mode/fortran/index.html), [Octave](http://codemirror.net/mode/octave/index.html) (Matlab), [TOML](http://codemirror.net/mode/toml/index.html), and [DTD](http://codemirror.net/mode/dtd/index.html). 618 * New addons: [`css-lint`](http://codemirror.net/addon/lint/css-lint.js), [`css-hint`](http://codemirror.net/doc/manual.html#addon_css-hint). 619 * Improve resilience to CSS 'frameworks' that globally mess up `box-sizing`. 620 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.16.0...3.17.0). 621 622 ## 3.16.0 (2013-08-21) 623 624 * The whole codebase is now under a single [license](http://codemirror.net/LICENSE) file. 625 * The project page was overhauled and redesigned. 626 * New themes: [Paraiso](http://codemirror.net/demo/theme.html#paraiso-dark) ([light](http://codemirror.net/demo/theme.html#paraiso-light)), [The Matrix](http://codemirror.net/demo/theme.html#the-matrix). 627 * Improved interaction between themes and [active-line](http://codemirror.net/doc/manual.html#addon_active-line)/[matchbrackets](http://codemirror.net/doc/manual.html#addon_matchbrackets) addons. 628 * New [folding](http://codemirror.net/doc/manual.html#addon_foldcode) function `CodeMirror.fold.comment`. 629 * Added [fullscreen](http://codemirror.net/doc/manual.html#addon_fullscreen) addon. 630 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.15.0...3.16.0). 631 632 ## 3.15.0 (2013-07-29) 633 634 * New modes: [Jade](http://codemirror.net/mode/jade/index.html), [Nginx](http://codemirror.net/mode/nginx/index.html). 635 * New addons: [Tern](http://codemirror.net/demo/tern.html), [matchtags](http://codemirror.net/doc/manual.html#addon_matchtags), and [foldgutter](http://codemirror.net/doc/manual.html#addon_foldgutter). 636 * Introduced [_helper_](http://codemirror.net/doc/manual.html#getHelper) concept ([context](https://groups.google.com/forum/#!msg/codemirror/cOc0xvUUEUU/nLrX1-qnidgJ)). 637 * New method: [`getModeAt`](http://codemirror.net/doc/manual.html#getModeAt). 638 * New themes: base16 [dark](http://codemirror.net/demo/theme.html#base16-dark)/[light](http://codemirror.net/demo/theme.html#base16-light), 3024 [dark](http://codemirror.net/demo/theme.html#3024-night)/[light](http://codemirror.net/demo/theme.html#3024-day), [tomorrow-night](http://codemirror.net/demo/theme.html#tomorrow-night-eighties). 639 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.14.0...3.15.0). 640 641 ## 3.14.0 (2013-06-20) 642 643 * New addons: [trailing space highlight](http://codemirror.net/doc/manual.html#addon_trailingspace), [XML completion](http://codemirror.net/doc/manual.html#addon_xml-hint) (rewritten), and [diff merging](http://codemirror.net/doc/manual.html#addon_merge). 644 * [`markText`](http://codemirror.net/doc/manual.html#markText) and [`addLineWidget`](http://codemirror.net/doc/manual.html#addLineWidget) now take a `handleMouseEvents` option. 645 * New methods: [`lineAtHeight`](http://codemirror.net/doc/manual.html#lineAtHeight), [`getTokenTypeAt`](http://codemirror.net/doc/manual.html#getTokenTypeAt). 646 * More precise cleanness-tracking using [`changeGeneration`](http://codemirror.net/doc/manual.html#changeGeneration) and [`isClean`](http://codemirror.net/doc/manual.html#isClean). 647 * Many extensions to [Emacs](http://codemirror.net/demo/emacs.html) mode (prefixes, more navigation units, and more). 648 * New events [`"keyHandled"`](http://codemirror.net/doc/manual.html#event_keyHandled) and [`"inputRead"`](http://codemirror.net/doc/manual.html#event_inputRead). 649 * Various improvements to [Ruby](http://codemirror.net/mode/ruby/index.html), [Smarty](http://codemirror.net/mode/smarty/index.html), [SQL](http://codemirror.net/mode/sql/index.html), and [Vim](http://codemirror.net/demo/vim.html) modes. 650 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/3.13.0...3.14.0). 651 652 ## 3.13.0 (2013-05-20) 653 654 * New modes: [COBOL](http://codemirror.net/mode/cobol/index.html) and [HAML](http://codemirror.net/mode/haml/index.html). 655 * New options: [`cursorScrollMargin`](http://codemirror.net/doc/manual.html#option_cursorScrollMargin) and [`coverGutterNextToScrollbar`](http://codemirror.net/doc/manual.html#option_coverGutterNextToScrollbar). 656 * New addon: [commenting](http://codemirror.net/doc/manual.html#addon_comment). 657 * More features added to the [Vim keymap](http://codemirror.net/demo/vim.html). 658 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.12...3.13.0). 659 660 ## 3.12.0 (2013-04-19) 661 662 * New mode: [GNU assembler](http://codemirror.net/mode/gas/index.html). 663 * New options: [`maxHighlightLength`](http://codemirror.net/doc/manual.html#option_maxHighlightLength) and [`historyEventDelay`](http://codemirror.net/doc/manual.html#option_historyEventDelay). 664 * Added [`addToHistory`](http://codemirror.net/doc/manual.html#mark_addToHistory) option for `markText`. 665 * Various fixes to JavaScript tokenization and indentation corner cases. 666 * Further improvements to the vim mode. 667 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.11...v3.12). 668 669 ## 3.11.0 (2013-03-20) 670 671 * **Removed code:** `collapserange`, `formatting`, and `simple-hint` addons. `plsql` and `mysql` modes (use [`sql`](http://codemirror.net/mode/sql/index.html) mode). 672 * **Moved code:** the range-finding functions for folding now have [their own files](http://codemirror.net/addon/fold/). 673 * **Changed interface:** the [`continuecomment`](http://codemirror.net/doc/manual.html#addon_continuecomment) addon now exposes an option, rather than a command. 674 * New modes: [SCSS](http://codemirror.net/mode/css/scss.html), [Tcl](http://codemirror.net/mode/tcl/index.html), [LiveScript](http://codemirror.net/mode/livescript/index.html), and [mIRC](http://codemirror.net/mode/mirc/index.html). 675 * New addons: [`placeholder`](http://codemirror.net/demo/placeholder.html), [HTML completion](http://codemirror.net/demo/html5complete.html). 676 * New methods: [`hasFocus`](http://codemirror.net/doc/manual.html#hasFocus), [`defaultCharWidth`](http://codemirror.net/doc/manual.html#defaultCharWidth). 677 * New events: [`beforeCursorEnter`](http://codemirror.net/doc/manual.html#event_beforeCursorEnter), [`renderLine`](http://codemirror.net/doc/manual.html#event_renderLine). 678 * Many improvements to the [`show-hint`](http://codemirror.net/doc/manual.html#addon_show-hint) completion dialog addon. 679 * Tweak behavior of by-word cursor motion. 680 * Further improvements to the [vim mode](http://codemirror.net/demo/vim.html). 681 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.1...v3.11). 682 683 ## 3.02.0 (2013-01-25) 684 685 Single-bugfix release. Fixes a problem that prevents CodeMirror instances from being garbage-collected after they become unused. 686 687 ## 3.01.0 (2013-01-21) 688 689 * Move all add-ons into an organized directory structure under [`/addon`](http://codemirror.net/addon/). **You might have to adjust your paths.** 690 * New modes: [D](http://codemirror.net/mode/d/index.html), [Sass](http://codemirror.net/mode/sass/index.html), [APL](http://codemirror.net/mode/apl/index.html), [SQL](http://codemirror.net/mode/sql/index.html) (configurable), and [Asterisk](http://codemirror.net/mode/asterisk/index.html). 691 * Several bugfixes in right-to-left text support. 692 * Add [`rtlMoveVisually`](http://codemirror.net/doc/manual.html#option_rtlMoveVisually) option. 693 * Improvements to vim keymap. 694 * Add built-in (lightweight) [overlay mode](http://codemirror.net/doc/manual.html#addOverlay) support. 695 * Support `showIfHidden` option for [line widgets](http://codemirror.net/doc/manual.html#addLineWidget). 696 * Add simple [Python hinter](http://codemirror.net/doc/manual.html#addon_python-hint). 697 * Bring back the [`fixedGutter`](http://codemirror.net/doc/manual.html#option_fixedGutter) option. 698 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.0...v3.01). 699 700 ## 3.1.0 (2013-02-21) 701 702 * **Incompatible:** key handlers may now _return_, rather than _throw_ `CodeMirror.Pass` to signal they didn't handle the key. 703 * Make documents a [first-class construct](http://codemirror.net/doc/manual.html#api_doc), support split views and subviews. 704 * Add a [new module](http://codemirror.net/doc/manual.html#addon_show-hint) for showing completion hints. Deprecate `simple-hint.js`. 705 * Extend [htmlmixed mode](http://codemirror.net/mode/htmlmixed/index.html) to allow custom handling of script types. 706 * Support an `insertLeft` option to [`setBookmark`](http://codemirror.net/doc/manual.html#setBookmark). 707 * Add an [`eachLine`](http://codemirror.net/doc/manual.html#eachLine) method to iterate over a document. 708 * New addon modules: [selection marking](http://codemirror.net/demo/markselection.html), [linting](http://codemirror.net/demo/lint.html), and [automatic bracket closing](http://codemirror.net/demo/closebrackets.html). 709 * Add [`"beforeChange"`](http://codemirror.net/doc/manual.html#event_beforeChange) and [`"beforeSelectionChange"`](http://codemirror.net/doc/manual.html#event_beforeSelectionChange) events. 710 * Add [`"hide"`](http://codemirror.net/doc/manual.html#event_hide) and [`"unhide"`](http://codemirror.net/doc/manual.html#event_unhide) events to marked ranges. 711 * Fix [`coordsChar`](http://codemirror.net/doc/manual.html#coordsChar)'s interpretation of its argument to match the documentation. 712 * New modes: [Turtle](http://codemirror.net/mode/turtle/index.html) and [Q](http://codemirror.net/mode/q/index.html). 713 * Further improvements to the [vim mode](http://codemirror.net/demo/vim.html). 714 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.01...v3.1). 715 716 ## 3.0.0 (2012-12-10) 717 718 **New major version**. Only partially backwards-compatible. See the [upgrading guide](http://codemirror.net/doc/upgrade_v3.html) for more information. Changes since release candidate 2: 719 720 * Rewritten VIM mode. 721 * Fix a few minor scrolling and sizing issues. 722 * Work around Safari segfault when dragging. 723 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v3.0rc2...v3.0). 724 725 ## 2.38.0 (2013-01-21) 726 727 Integrate some bugfixes, enhancements to the vim keymap, and new modes ([D](http://codemirror.net/mode/d/index.html), [Sass](http://codemirror.net/mode/sass/index.html), [APL](http://codemirror.net/mode/apl/index.html)) from the v3 branch. 728 729 ## 2.37.0 (2012-12-20) 730 731 * New mode: [SQL](http://codemirror.net/mode/sql/index.html) (will replace [plsql](http://codemirror.net/mode/plsql/index.html) and [mysql](http://codemirror.net/mode/mysql/index.html) modes). 732 * Further work on the new VIM mode. 733 * Fix Cmd/Ctrl keys on recent Operas on OS X. 734 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v2.36...v2.37). 735 736 ## 2.36.0 (2012-11-20) 737 738 * New mode: [Z80 assembly](http://codemirror.net/mode/z80/index.html). 739 * New theme: [Twilight](http://codemirror.net/demo/theme.html#twilight). 740 * Add command-line compression helper. 741 * Make [`scrollIntoView`](http://codemirror.net/doc/manual.html#scrollIntoView) public. 742 * Add [`defaultTextHeight`](http://codemirror.net/doc/manual.html#defaultTextHeight) method. 743 * Various extensions to the vim keymap. 744 * Make [PHP mode](http://codemirror.net/mode/php/index.html) build on [mixed HTML mode](http://codemirror.net/mode/htmlmixed/index.html). 745 * Add [comment-continuing](http://codemirror.net/doc/manual.html#addon_continuecomment) add-on. 746 * Full [list of patches](http://codemirror.net/https://github.com/codemirror/CodeMirror/compare/v2.35...v2.36). 747 748 ## 2.35.0 (2012-10-22) 749 750 * New (sub) mode: [TypeScript](http://codemirror.net/mode/javascript/typescript.html). 751 * Don't overwrite (insert key) when pasting. 752 * Fix several bugs in [`markText`](http://codemirror.net/doc/manual.html#markText)/undo interaction. 753 * Better indentation of JavaScript code without semicolons. 754 * Add [`defineInitHook`](http://codemirror.net/doc/manual.html#defineInitHook) function. 755 * Full [list of patches](https://github.com/codemirror/CodeMirror/compare/v2.34...v2.35). 756 757 ## 2.34.0 (2012-09-19) 758 759 * New mode: [Common Lisp](http://codemirror.net/mode/commonlisp/index.html). 760 * Fix right-click select-all on most browsers. 761 * Change the way highlighting happens: 762 Saves memory and CPU cycles. 763 `compareStates` is no longer needed. 764 `onHighlightComplete` no longer works. 765 * Integrate mode (Markdown, XQuery, CSS, sTex) tests in central testsuite. 766 * Add a [`CodeMirror.version`](http://codemirror.net/doc/manual.html#version) property. 767 * More robust handling of nested modes in [formatting](http://codemirror.net/demo/formatting.html) and [closetag](http://codemirror.net/demo/closetag.html) plug-ins. 768 * Un/redo now preserves [marked text](http://codemirror.net/doc/manual.html#markText) and bookmarks. 769 * [Full list](https://github.com/codemirror/CodeMirror/compare/v2.33...v2.34) of patches. 770 771 ## 2.33.0 (2012-08-23) 772 773 * New mode: [Sieve](http://codemirror.net/mode/sieve/index.html). 774 * New [`getViewPort`](http://codemirror.net/doc/manual.html#getViewport) and [`onViewportChange`](http://codemirror.net/doc/manual.html#option_onViewportChange) API. 775 * [Configurable](http://codemirror.net/doc/manual.html#option_cursorBlinkRate) cursor blink rate. 776 * Make binding a key to `false` disabling handling (again). 777 * Show non-printing characters as red dots. 778 * More tweaks to the scrolling model. 779 * Expanded testsuite. Basic linter added. 780 * Remove most uses of `innerHTML`. Remove `CodeMirror.htmlEscape`. 781 * [Full list](https://github.com/codemirror/CodeMirror/compare/v2.32...v2.33) of patches. 782 783 ## 2.32.0 (2012-07-23) 784 785 Emergency fix for a bug where an editor with line wrapping on IE will break when there is _no_ scrollbar. 786 787 ## 2.31.0 (2012-07-20) 788 789 * New modes: [OCaml](http://codemirror.net/mode/ocaml/index.html), [Haxe](http://codemirror.net/mode/haxe/index.html), and [VB.NET](http://codemirror.net/mode/vb/index.html). 790 * Several fixes to the new scrolling model. 791 * Add a [`setSize`](http://codemirror.net/doc/manual.html#setSize) method for programmatic resizing. 792 * Add [`getHistory`](http://codemirror.net/doc/manual.html#getHistory) and [`setHistory`](http://codemirror.net/doc/manual.html#setHistory) methods. 793 * Allow custom line separator string in [`getValue`](http://codemirror.net/doc/manual.html#getValue) and [`getRange`](http://codemirror.net/doc/manual.html#getRange). 794 * Support double- and triple-click drag, double-clicking whitespace. 795 * And more... [(all patches)](https://github.com/codemirror/CodeMirror/compare/v2.3...v2.31) 796 797 ## 2.30.0 (2012-06-22) 798 799 * **New scrollbar implementation**. Should flicker less. Changes DOM structure of the editor. 800 * New theme: [vibrant-ink](http://codemirror.net/demo/theme.html#vibrant-ink). 801 * Many extensions to the VIM keymap (including text objects). 802 * Add [mode-multiplexing](http://codemirror.net/demo/multiplex.html) utility script. 803 * Fix bug where right-click paste works in read-only mode. 804 * Add a [`getScrollInfo`](http://codemirror.net/doc/manual.html#getScrollInfo) method. 805 * Lots of other [fixes](https://github.com/codemirror/CodeMirror/compare/v2.25...v2.3). 806 807 ## 2.25.0 (2012-05-23) 808 809 * New mode: [Erlang](http://codemirror.net/mode/erlang/index.html). 810 * **Remove xmlpure mode** (use [xml.js](http://codemirror.net/mode/xml/index.html)). 811 * Fix line-wrapping in Opera. 812 * Fix X Windows middle-click paste in Chrome. 813 * Fix bug that broke pasting of huge documents. 814 * Fix backspace and tab key repeat in Opera. 815 816 ## 2.24.0 (2012-04-23) 817 818 * **Drop support for Internet Explorer 6**. 819 * New modes: [Shell](http://codemirror.net/mode/shell/index.html), [Tiki wiki](http://codemirror.net/mode/tiki/index.html), [Pig Latin](http://codemirror.net/mode/pig/index.html). 820 * New themes: [Ambiance](http://codemirror.net/demo/theme.html#ambiance), [Blackboard](http://codemirror.net/demo/theme.html#blackboard). 821 * More control over drag/drop with [`dragDrop`](http://codemirror.net/doc/manual.html#option_dragDrop) and [`onDragEvent`](http://codemirror.net/doc/manual.html#option_onDragEvent) options. 822 * Make HTML mode a bit less pedantic. 823 * Add [`compoundChange`](http://codemirror.net/doc/manual.html#compoundChange) API method. 824 * Several fixes in undo history and line hiding. 825 * Remove (broken) support for `catchall` in key maps, add `nofallthrough` boolean field instead. 826 827 ## 2.23.0 (2012-03-26) 828 829 * Change **default binding for tab**. Starting in 2.23, these bindings are default: 830 * Tab: Insert tab character 831 * Shift-tab: Reset line indentation to default 832 * Ctrl/Cmd-[: Reduce line indentation (old tab behaviour) 833 * Ctrl/Cmd-]: Increase line indentation (old shift-tab behaviour) 834 * New modes: [XQuery](http://codemirror.net/mode/xquery/index.html) and [VBScript](http://codemirror.net/mode/vbscript/index.html). 835 * Two new themes: [lesser-dark](http://codemirror.net/mode/less/index.html) and [xq-dark](http://codemirror.net/mode/xquery/index.html). 836 * Differentiate between background and text styles in [`setLineClass`](http://codemirror.net/doc/manual.html#setLineClass). 837 * Fix drag-and-drop in IE9+. 838 * Extend [`charCoords`](http://codemirror.net/doc/manual.html#charCoords) and [`cursorCoords`](http://codemirror.net/doc/manual.html#cursorCoords) with a `mode` argument. 839 * Add [`autofocus`](http://codemirror.net/doc/manual.html#option_autofocus) option. 840 * Add [`findMarksAt`](http://codemirror.net/doc/manual.html#findMarksAt) method. 841 842 ## 2.22.0 (2012-02-27) 843 844 * Allow [key handlers](http://codemirror.net/doc/manual.html#keymaps) to pass up events, allow binding characters. 845 * Add [`autoClearEmptyLines`](http://codemirror.net/doc/manual.html#option_autoClearEmptyLines) option. 846 * Properly use tab stops when rendering tabs. 847 * Make PHP mode more robust. 848 * Support indentation blocks in [code folder](http://codemirror.net/doc/manual.html#addon_foldcode). 849 * Add a script for [highlighting instances of the selection](http://codemirror.net/doc/manual.html#addon_match-highlighter). 850 * New [.properties](http://codemirror.net/mode/properties/index.html) mode. 851 * Fix many bugs. 852 853 ## 2.21.0 (2012-01-27) 854 855 * Added [LESS](http://codemirror.net/mode/less/index.html), [MySQL](http://codemirror.net/mode/mysql/index.html), [Go](http://codemirror.net/mode/go/index.html), and [Verilog](http://codemirror.net/mode/verilog/index.html) modes. 856 * Add [`smartIndent`](http://codemirror.net/doc/manual.html#option_smartIndent) option. 857 * Support a cursor in [`readOnly`](http://codemirror.net/doc/manual.html#option_readOnly)-mode. 858 * Support assigning multiple styles to a token. 859 * Use a new approach to drawing the selection. 860 * Add [`scrollTo`](http://codemirror.net/doc/manual.html#scrollTo) method. 861 * Allow undo/redo events to span non-adjacent lines. 862 * Lots and lots of bugfixes. 863 864 ## 2.20.0 (2011-12-20) 865 866 * Slightly incompatible API changes. Read [this](http://codemirror.net/doc/upgrade_v2.2.html). 867 * New approach to [binding](http://codemirror.net/doc/manual.html#option_extraKeys) keys, support for [custom bindings](http://codemirror.net/doc/manual.html#option_keyMap). 868 * Support for overwrite (insert). 869 * [Custom-width](http://codemirror.net/doc/manual.html#option_tabSize) and [stylable](http://codemirror.net/demo/visibletabs.html) tabs. 870 * Moved more code into [add-on scripts](http://codemirror.net/doc/manual.html#addons). 871 * Support for sane vertical cursor movement in wrapped lines. 872 * More reliable handling of editing [marked text](http://codemirror.net/doc/manual.html#markText). 873 * Add minimal [emacs](http://codemirror.net/demo/emacs.html) and [vim](http://codemirror.net/demo/vim.html) bindings. 874 * Rename `coordsFromIndex` to [`posFromIndex`](http://codemirror.net/doc/manual.html#posFromIndex), add [`indexFromPos`](http://codemirror.net/doc/manual.html#indexFromPos) method. 875 876 ## 2.18.0 (2011-11-21) 877 878 Fixes `TextMarker.clear`, which is broken in 2.17. 879 880 ## 2.17.0 (2011-11-21) 881 882 * Add support for [line wrapping](http://codemirror.net/doc/manual.html#option_lineWrapping) and [code folding](http://codemirror.net/doc/manual.html#hideLine). 883 * Add [Github-style Markdown](http://codemirror.net/mode/gfm/index.html) mode. 884 * Add [Monokai](http://codemirror.net/theme/monokai.css) and [Rubyblue](http://codemirror.net/theme/rubyblue.css) themes. 885 * Add [`setBookmark`](http://codemirror.net/doc/manual.html#setBookmark) method. 886 * Move some of the demo code into reusable components under [`lib/util`](http://codemirror.net/addon/). 887 * Make screen-coord-finding code faster and more reliable. 888 * Fix drag-and-drop in Firefox. 889 * Improve support for IME. 890 * Speed up content rendering. 891 * Fix browser's built-in search in Webkit. 892 * Make double- and triple-click work in IE. 893 * Various fixes to modes. 894 895 ## 2.16.0 (2011-10-27) 896 897 * Add [Perl](http://codemirror.net/mode/perl/index.html), [Rust](http://codemirror.net/mode/rust/index.html), [TiddlyWiki](http://codemirror.net/mode/tiddlywiki/index.html), and [Groovy](http://codemirror.net/mode/groovy/index.html) modes. 898 * Dragging text inside the editor now moves, rather than copies. 899 * Add a [`coordsFromIndex`](http://codemirror.net/doc/manual.html#coordsFromIndex) method. 900 * **API change**: `setValue` now no longer clears history. Use [`clearHistory`](http://codemirror.net/doc/manual.html#clearHistory) for that. 901 * **API change**: [`markText`](http://codemirror.net/doc/manual.html#markText) now returns an object with `clear` and `find` methods. Marked text is now more robust when edited. 902 * Fix editing code with tabs in Internet Explorer. 903 904 ## 2.15.0 (2011-09-26) 905 906 Fix bug that snuck into 2.14: Clicking the character that currently has the cursor didn't re-focus the editor. 907 908 ## 2.14.0 (2011-09-26) 909 910 * Add [Clojure](http://codemirror.net/mode/clojure/index.html), [Pascal](http://codemirror.net/mode/pascal/index.html), [NTriples](http://codemirror.net/mode/ntriples/index.html), [Jinja2](http://codemirror.net/mode/jinja2/index.html), and [Markdown](http://codemirror.net/mode/markdown/index.html) modes. 911 * Add [Cobalt](http://codemirror.net/theme/cobalt.css) and [Eclipse](http://codemirror.net/theme/eclipse.css) themes. 912 * Add a [`fixedGutter`](http://codemirror.net/doc/manual.html#option_fixedGutter) option. 913 * Fix bug with `setValue` breaking cursor movement. 914 * Make gutter updates much more efficient. 915 * Allow dragging of text out of the editor (on modern browsers). 916 917 ## 2.13.0 (2011-08-23) 918 919 * Add [Ruby](http://codemirror.net/mode/ruby/index.html), [R](http://codemirror.net/mode/r/index.html), [CoffeeScript](http://codemirror.net/mode/coffeescript/index.html), and [Velocity](http://codemirror.net/mode/velocity/index.html) modes. 920 * Add [`getGutterElement`](http://codemirror.net/doc/manual.html#getGutterElement) to API. 921 * Several fixes to scrolling and positioning. 922 * Add [`smartHome`](http://codemirror.net/doc/manual.html#option_smartHome) option. 923 * Add an experimental [pure XML](http://codemirror.net/mode/xmlpure/index.html) mode. 924 925 ## 2.12.0 (2011-07-25) 926 927 * Add a [SPARQL](http://codemirror.net/mode/sparql/index.html) mode. 928 * Fix bug with cursor jumping around in an unfocused editor in IE. 929 * Allow key and mouse events to bubble out of the editor. Ignore widget clicks. 930 * Solve cursor flakiness after undo/redo. 931 * Fix block-reindent ignoring the last few lines. 932 * Fix parsing of multi-line attrs in XML mode. 933 * Use `innerHTML` for HTML-escaping. 934 * Some fixes to indentation in C-like mode. 935 * Shrink horiz scrollbars when long lines removed. 936 * Fix width feedback loop bug that caused the width of an inner DIV to shrink. 937 938 ## 2.11.0 (2011-07-04) 939 940 * Add a [Scheme mode](http://codemirror.net/mode/scheme/index.html). 941 * Add a `replace` method to search cursors, for cursor-preserving replacements. 942 * Make the [C-like mode](http://codemirror.net/mode/clike/index.html) mode more customizable. 943 * Update XML mode to spot mismatched tags. 944 * Add `getStateAfter` API and `compareState` mode API methods for finer-grained mode magic. 945 * Add a `getScrollerElement` API method to manipulate the scrolling DIV. 946 * Fix drag-and-drop for Firefox. 947 * Add a C# configuration for the [C-like mode](http://codemirror.net/mode/clike/index.html). 948 * Add [full-screen editing](http://codemirror.net/demo/fullscreen.html) and [mode-changing](http://codemirror.net/demo/changemode.html) demos. 949 950 ## 2.10.0 (2011-06-07) 951 952 Add a [theme](http://codemirror.net/doc/manual.html#option_theme) system ([demo](http://codemirror.net/demo/theme.html)). Note that this is not backwards-compatible—you'll have to update your styles and modes! 953 954 ## 2.2.0 (2011-06-07) 955 956 * Add a [Lua mode](http://codemirror.net/mode/lua/index.html). 957 * Fix reverse-searching for a regexp. 958 * Empty lines can no longer break highlighting. 959 * Rework scrolling model (the outer wrapper no longer does the scrolling). 960 * Solve horizontal jittering on long lines. 961 * Add [runmode.js](http://codemirror.net/demo/runmode.html). 962 * Immediately re-highlight text when typing. 963 * Fix problem with 'sticking' horizontal scrollbar. 964 965 ## 2.1.0 (2011-05-26) 966 967 * Add a [Smalltalk mode](http://codemirror.net/mode/smalltalk/index.html). 968 * Add a [reStructuredText mode](http://codemirror.net/mode/rst/index.html). 969 * Add a [Python mode](http://codemirror.net/mode/python/index.html). 970 * Add a [PL/SQL mode](http://codemirror.net/mode/plsql/index.html). 971 * `coordsChar` now works 972 * Fix a problem where `onCursorActivity` interfered with `onChange`. 973 * Fix a number of scrolling and mouse-click-position glitches. 974 * Pass information about the changed lines to `onChange`. 975 * Support cmd-up/down on OS X. 976 * Add triple-click line selection. 977 * Don't handle shift when changing the selection through the API. 978 * Support `"nocursor"` mode for `readOnly` option. 979 * Add an `onHighlightComplete` option. 980 * Fix the context menu for Firefox. 981 982 ## 2.0.0 (2011-03-28) 983 984 CodeMirror 2 is a complete rewrite that's faster, smaller, simpler to use, and less dependent on browser quirks. See [this](http://codemirror.net/doc/internals.html) and [this](http://groups.google.com/group/codemirror/browse_thread/thread/5a8e894024a9f580) for more information. -
src/wp-includes/js/codemirror/CONTRIBUTING.md
1 # How to contribute 2 3 - [Getting help](#getting-help) 4 - [Submitting bug reports](#submitting-bug-reports) 5 - [Contributing code](#contributing-code) 6 7 ## Getting help 8 9 Community discussion, questions, and informal bug reporting is done on the 10 [discuss.CodeMirror forum](http://discuss.codemirror.net). 11 12 ## Submitting bug reports 13 14 The preferred way to report bugs is to use the 15 [GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before 16 reporting a bug, read these pointers. 17 18 **Note:** The issue tracker is for *bugs*, not requests for help. Questions 19 should be asked on the 20 [discuss.CodeMirror forum](http://discuss.codemirror.net) instead. 21 22 ### Reporting bugs effectively 23 24 - CodeMirror is maintained by volunteers. They don't owe you anything, so be 25 polite. Reports with an indignant or belligerent tone tend to be moved to the 26 bottom of the pile. 27 28 - Include information about **the browser in which the problem occurred**. Even 29 if you tested several browsers, and the problem occurred in all of them, 30 mention this fact in the bug report. Also include browser version numbers and 31 the operating system that you're on. 32 33 - Mention which release of CodeMirror you're using. Preferably, try also with 34 the current development snapshot, to ensure the problem has not already been 35 fixed. 36 37 - Mention very precisely what went wrong. "X is broken" is not a good bug 38 report. What did you expect to happen? What happened instead? Describe the 39 exact steps a maintainer has to take to make the problem occur. We can not 40 fix something that we can not observe. 41 42 - If the problem can not be reproduced in any of the demos included in the 43 CodeMirror distribution, please provide an HTML document that demonstrates 44 the problem. The best way to do this is to go to 45 [jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and 46 include the resulting link in your bug report. 47 48 ## Contributing code 49 50 - Make sure you have a [GitHub Account](https://github.com/signup/free) 51 - Fork [CodeMirror](https://github.com/codemirror/CodeMirror/) 52 ([how to fork a repo](https://help.github.com/articles/fork-a-repo)) 53 - Make your changes 54 - If your changes are easy to test or likely to regress, add tests. 55 Tests for the core go into `test/test.js`, some modes have their own 56 test suite under `mode/XXX/test.js`. Feel free to add new test 57 suites to modes that don't have one yet (be sure to link the new 58 tests into `test/index.html`). 59 - Follow the general code style of the rest of the project (see 60 below). Run `bin/lint` to verify that the linter is happy. 61 - Make sure all tests pass. Visit `test/index.html` in your browser to 62 run them. 63 - Submit a pull request 64 ([how to create a pull request](https://help.github.com/articles/fork-a-repo)). 65 Don't put more than one feature/fix in a single pull request. 66 67 By contributing code to CodeMirror you 68 69 - agree to license the contributed code under CodeMirror's [MIT 70 license](http://codemirror.net/LICENSE). 71 72 - confirm that you have the right to contribute and license the code 73 in question. (Either you hold all rights on the code, or the rights 74 holder has explicitly granted the right to use it like this, 75 through a compatible open source license or through a direct 76 agreement with you.) 77 78 ### Coding standards 79 80 - 2 spaces per indentation level, no tabs. 81 82 - Note that the linter (`bin/lint`) which is run after each commit 83 complains about unused variables and functions. Prefix their names 84 with an underscore to muffle it. 85 86 - CodeMirror does *not* follow JSHint or JSLint prescribed style. 87 Patches that try to 'fix' code to pass one of these linters will be 88 unceremoniously discarded. -
src/wp-includes/js/codemirror/LICENSE
1 Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others 2 3 Permission is hereby granted, free of charge, to any person obtaining a copy 4 of this software and associated documentation files (the "Software"), to deal 5 in the Software without restriction, including without limitation the rights 6 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 copies of the Software, and to permit persons to whom the Software is 8 furnished to do so, subject to the following conditions: 9 10 The above copyright notice and this permission notice shall be included in 11 all copies or substantial portions of the Software. 12 13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 THE SOFTWARE. -
src/wp-includes/js/codemirror/addon/comment/comment.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var noOptions = {}; 15 var nonWS = /[^\s\u00a0]/; 16 var Pos = CodeMirror.Pos; 17 18 function firstNonWS(str) { 19 var found = str.search(nonWS); 20 return found == -1 ? 0 : found; 21 } 22 23 CodeMirror.commands.toggleComment = function(cm) { 24 cm.toggleComment(); 25 }; 26 27 CodeMirror.defineExtension("toggleComment", function(options) { 28 if (!options) options = noOptions; 29 var cm = this; 30 var minLine = Infinity, ranges = this.listSelections(), mode = null; 31 for (var i = ranges.length - 1; i >= 0; i--) { 32 var from = ranges[i].from(), to = ranges[i].to(); 33 if (from.line >= minLine) continue; 34 if (to.line >= minLine) to = Pos(minLine, 0); 35 minLine = from.line; 36 if (mode == null) { 37 if (cm.uncomment(from, to, options)) mode = "un"; 38 else { cm.lineComment(from, to, options); mode = "line"; } 39 } else if (mode == "un") { 40 cm.uncomment(from, to, options); 41 } else { 42 cm.lineComment(from, to, options); 43 } 44 } 45 }); 46 47 // Rough heuristic to try and detect lines that are part of multi-line string 48 function probablyInsideString(cm, pos, line) { 49 return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"`]/.test(line) 50 } 51 52 CodeMirror.defineExtension("lineComment", function(from, to, options) { 53 if (!options) options = noOptions; 54 var self = this, mode = self.getModeAt(from); 55 var firstLine = self.getLine(from.line); 56 if (firstLine == null || probablyInsideString(self, from, firstLine)) return; 57 58 var commentString = options.lineComment || mode.lineComment; 59 if (!commentString) { 60 if (options.blockCommentStart || mode.blockCommentStart) { 61 options.fullLines = true; 62 self.blockComment(from, to, options); 63 } 64 return; 65 } 66 67 var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); 68 var pad = options.padding == null ? " " : options.padding; 69 var blankLines = options.commentBlankLines || from.line == to.line; 70 71 self.operation(function() { 72 if (options.indent) { 73 var baseString = null; 74 for (var i = from.line; i < end; ++i) { 75 var line = self.getLine(i); 76 var whitespace = line.slice(0, firstNonWS(line)); 77 if (baseString == null || baseString.length > whitespace.length) { 78 baseString = whitespace; 79 } 80 } 81 for (var i = from.line; i < end; ++i) { 82 var line = self.getLine(i), cut = baseString.length; 83 if (!blankLines && !nonWS.test(line)) continue; 84 if (line.slice(0, cut) != baseString) cut = firstNonWS(line); 85 self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); 86 } 87 } else { 88 for (var i = from.line; i < end; ++i) { 89 if (blankLines || nonWS.test(self.getLine(i))) 90 self.replaceRange(commentString + pad, Pos(i, 0)); 91 } 92 } 93 }); 94 }); 95 96 CodeMirror.defineExtension("blockComment", function(from, to, options) { 97 if (!options) options = noOptions; 98 var self = this, mode = self.getModeAt(from); 99 var startString = options.blockCommentStart || mode.blockCommentStart; 100 var endString = options.blockCommentEnd || mode.blockCommentEnd; 101 if (!startString || !endString) { 102 if ((options.lineComment || mode.lineComment) && options.fullLines != false) 103 self.lineComment(from, to, options); 104 return; 105 } 106 if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return 107 108 var end = Math.min(to.line, self.lastLine()); 109 if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; 110 111 var pad = options.padding == null ? " " : options.padding; 112 if (from.line > end) return; 113 114 self.operation(function() { 115 if (options.fullLines != false) { 116 var lastLineHasText = nonWS.test(self.getLine(end)); 117 self.replaceRange(pad + endString, Pos(end)); 118 self.replaceRange(startString + pad, Pos(from.line, 0)); 119 var lead = options.blockCommentLead || mode.blockCommentLead; 120 if (lead != null) for (var i = from.line + 1; i <= end; ++i) 121 if (i != end || lastLineHasText) 122 self.replaceRange(lead + pad, Pos(i, 0)); 123 } else { 124 self.replaceRange(endString, to); 125 self.replaceRange(startString, from); 126 } 127 }); 128 }); 129 130 CodeMirror.defineExtension("uncomment", function(from, to, options) { 131 if (!options) options = noOptions; 132 var self = this, mode = self.getModeAt(from); 133 var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end); 134 135 // Try finding line comments 136 var lineString = options.lineComment || mode.lineComment, lines = []; 137 var pad = options.padding == null ? " " : options.padding, didSomething; 138 lineComment: { 139 if (!lineString) break lineComment; 140 for (var i = start; i <= end; ++i) { 141 var line = self.getLine(i); 142 var found = line.indexOf(lineString); 143 if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; 144 if (found == -1 && nonWS.test(line)) break lineComment; 145 if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; 146 lines.push(line); 147 } 148 self.operation(function() { 149 for (var i = start; i <= end; ++i) { 150 var line = lines[i - start]; 151 var pos = line.indexOf(lineString), endPos = pos + lineString.length; 152 if (pos < 0) continue; 153 if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; 154 didSomething = true; 155 self.replaceRange("", Pos(i, pos), Pos(i, endPos)); 156 } 157 }); 158 if (didSomething) return true; 159 } 160 161 // Try block comments 162 var startString = options.blockCommentStart || mode.blockCommentStart; 163 var endString = options.blockCommentEnd || mode.blockCommentEnd; 164 if (!startString || !endString) return false; 165 var lead = options.blockCommentLead || mode.blockCommentLead; 166 var startLine = self.getLine(start), open = startLine.indexOf(startString) 167 if (open == -1) return false 168 var endLine = end == start ? startLine : self.getLine(end) 169 var close = endLine.indexOf(endString, end == start ? open + startString.length : 0); 170 if (close == -1 && start != end) { 171 endLine = self.getLine(--end); 172 close = endLine.indexOf(endString); 173 } 174 if (close == -1 || 175 !/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) || 176 !/comment/.test(self.getTokenTypeAt(Pos(end, close + 1)))) 177 return false; 178 179 // Avoid killing block comments completely outside the selection. 180 // Positions of the last startString before the start of the selection, and the first endString after it. 181 var lastStart = startLine.lastIndexOf(startString, from.ch); 182 var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length); 183 if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false; 184 // Positions of the first endString after the end of the selection, and the last startString before it. 185 firstEnd = endLine.indexOf(endString, to.ch); 186 var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch); 187 lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart; 188 if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false; 189 190 self.operation(function() { 191 self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), 192 Pos(end, close + endString.length)); 193 var openEnd = open + startString.length; 194 if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; 195 self.replaceRange("", Pos(start, open), Pos(start, openEnd)); 196 if (lead) for (var i = start + 1; i <= end; ++i) { 197 var line = self.getLine(i), found = line.indexOf(lead); 198 if (found == -1 || nonWS.test(line.slice(0, found))) continue; 199 var foundEnd = found + lead.length; 200 if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; 201 self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); 202 } 203 }); 204 return true; 205 }); 206 }); -
src/wp-includes/js/codemirror/addon/comment/continuecomment.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 var modes = ["clike", "css", "javascript"]; 13 14 for (var i = 0; i < modes.length; ++i) 15 CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); 16 17 function continueComment(cm) { 18 if (cm.getOption("disableInput")) return CodeMirror.Pass; 19 var ranges = cm.listSelections(), mode, inserts = []; 20 for (var i = 0; i < ranges.length; i++) { 21 var pos = ranges[i].head, token = cm.getTokenAt(pos); 22 if (token.type != "comment") return CodeMirror.Pass; 23 var modeHere = CodeMirror.innerMode(cm.getMode(), token.state).mode; 24 if (!mode) mode = modeHere; 25 else if (mode != modeHere) return CodeMirror.Pass; 26 27 var insert = null; 28 if (mode.blockCommentStart && mode.blockCommentContinue) { 29 var end = token.string.indexOf(mode.blockCommentEnd); 30 var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; 31 if (end != -1 && end == token.string.length - mode.blockCommentEnd.length && pos.ch >= end) { 32 // Comment ended, don't continue it 33 } else if (token.string.indexOf(mode.blockCommentStart) == 0) { 34 insert = full.slice(0, token.start); 35 if (!/^\s*$/.test(insert)) { 36 insert = ""; 37 for (var j = 0; j < token.start; ++j) insert += " "; 38 } 39 } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && 40 found + mode.blockCommentContinue.length > token.start && 41 /^\s*$/.test(full.slice(0, found))) { 42 insert = full.slice(0, found); 43 } 44 if (insert != null) insert += mode.blockCommentContinue; 45 } 46 if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) { 47 var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); 48 if (found > -1) { 49 insert = line.slice(0, found); 50 if (/\S/.test(insert)) insert = null; 51 else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0]; 52 } 53 } 54 if (insert == null) return CodeMirror.Pass; 55 inserts[i] = "\n" + insert; 56 } 57 58 cm.operation(function() { 59 for (var i = ranges.length - 1; i >= 0; i--) 60 cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert"); 61 }); 62 } 63 64 function continueLineCommentEnabled(cm) { 65 var opt = cm.getOption("continueComments"); 66 if (opt && typeof opt == "object") 67 return opt.continueLineComment !== false; 68 return true; 69 } 70 71 CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { 72 if (prev && prev != CodeMirror.Init) 73 cm.removeKeyMap("continueComment"); 74 if (val) { 75 var key = "Enter"; 76 if (typeof val == "string") 77 key = val; 78 else if (typeof val == "object" && val.key) 79 key = val.key; 80 var map = {name: "continueComment"}; 81 map[key] = continueComment; 82 cm.addKeyMap(map); 83 } 84 }); 85 }); -
src/wp-includes/js/codemirror/addon/edit/closebrackets.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 var defaults = { 13 pairs: "()[]{}''\"\"", 14 triples: "", 15 explode: "[]{}" 16 }; 17 18 var Pos = CodeMirror.Pos; 19 20 CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { 21 if (old && old != CodeMirror.Init) { 22 cm.removeKeyMap(keyMap); 23 cm.state.closeBrackets = null; 24 } 25 if (val) { 26 cm.state.closeBrackets = val; 27 cm.addKeyMap(keyMap); 28 } 29 }); 30 31 function getOption(conf, name) { 32 if (name == "pairs" && typeof conf == "string") return conf; 33 if (typeof conf == "object" && conf[name] != null) return conf[name]; 34 return defaults[name]; 35 } 36 37 var bind = defaults.pairs + "`"; 38 var keyMap = {Backspace: handleBackspace, Enter: handleEnter}; 39 for (var i = 0; i < bind.length; i++) 40 keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i)); 41 42 function handler(ch) { 43 return function(cm) { return handleChar(cm, ch); }; 44 } 45 46 function getConfig(cm) { 47 var deflt = cm.state.closeBrackets; 48 if (!deflt || deflt.override) return deflt; 49 var mode = cm.getModeAt(cm.getCursor()); 50 return mode.closeBrackets || deflt; 51 } 52 53 function handleBackspace(cm) { 54 var conf = getConfig(cm); 55 if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass; 56 57 var pairs = getOption(conf, "pairs"); 58 var ranges = cm.listSelections(); 59 for (var i = 0; i < ranges.length; i++) { 60 if (!ranges[i].empty()) return CodeMirror.Pass; 61 var around = charsAround(cm, ranges[i].head); 62 if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; 63 } 64 for (var i = ranges.length - 1; i >= 0; i--) { 65 var cur = ranges[i].head; 66 cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete"); 67 } 68 } 69 70 function handleEnter(cm) { 71 var conf = getConfig(cm); 72 var explode = conf && getOption(conf, "explode"); 73 if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass; 74 75 var ranges = cm.listSelections(); 76 for (var i = 0; i < ranges.length; i++) { 77 if (!ranges[i].empty()) return CodeMirror.Pass; 78 var around = charsAround(cm, ranges[i].head); 79 if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass; 80 } 81 cm.operation(function() { 82 cm.replaceSelection("\n\n", null); 83 cm.execCommand("goCharLeft"); 84 ranges = cm.listSelections(); 85 for (var i = 0; i < ranges.length; i++) { 86 var line = ranges[i].head.line; 87 cm.indentLine(line, null, true); 88 cm.indentLine(line + 1, null, true); 89 } 90 }); 91 } 92 93 function contractSelection(sel) { 94 var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0; 95 return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)), 96 head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))}; 97 } 98 99 function handleChar(cm, ch) { 100 var conf = getConfig(cm); 101 if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass; 102 103 var pairs = getOption(conf, "pairs"); 104 var pos = pairs.indexOf(ch); 105 if (pos == -1) return CodeMirror.Pass; 106 var triples = getOption(conf, "triples"); 107 108 var identical = pairs.charAt(pos + 1) == ch; 109 var ranges = cm.listSelections(); 110 var opening = pos % 2 == 0; 111 112 var type; 113 for (var i = 0; i < ranges.length; i++) { 114 var range = ranges[i], cur = range.head, curType; 115 var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); 116 if (opening && !range.empty()) { 117 curType = "surround"; 118 } else if ((identical || !opening) && next == ch) { 119 if (identical && stringStartsAfter(cm, cur)) 120 curType = "both"; 121 else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch) 122 curType = "skipThree"; 123 else 124 curType = "skip"; 125 } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 && 126 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch && 127 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) { 128 curType = "addFour"; 129 } else if (identical) { 130 if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both"; 131 else return CodeMirror.Pass; 132 } else if (opening && (cm.getLine(cur.line).length == cur.ch || 133 isClosingBracket(next, pairs) || 134 /\s/.test(next))) { 135 curType = "both"; 136 } else { 137 return CodeMirror.Pass; 138 } 139 if (!type) type = curType; 140 else if (type != curType) return CodeMirror.Pass; 141 } 142 143 var left = pos % 2 ? pairs.charAt(pos - 1) : ch; 144 var right = pos % 2 ? ch : pairs.charAt(pos + 1); 145 cm.operation(function() { 146 if (type == "skip") { 147 cm.execCommand("goCharRight"); 148 } else if (type == "skipThree") { 149 for (var i = 0; i < 3; i++) 150 cm.execCommand("goCharRight"); 151 } else if (type == "surround") { 152 var sels = cm.getSelections(); 153 for (var i = 0; i < sels.length; i++) 154 sels[i] = left + sels[i] + right; 155 cm.replaceSelections(sels, "around"); 156 sels = cm.listSelections().slice(); 157 for (var i = 0; i < sels.length; i++) 158 sels[i] = contractSelection(sels[i]); 159 cm.setSelections(sels); 160 } else if (type == "both") { 161 cm.replaceSelection(left + right, null); 162 cm.triggerElectric(left + right); 163 cm.execCommand("goCharLeft"); 164 } else if (type == "addFour") { 165 cm.replaceSelection(left + left + left + left, "before"); 166 cm.execCommand("goCharRight"); 167 } 168 }); 169 } 170 171 function isClosingBracket(ch, pairs) { 172 var pos = pairs.lastIndexOf(ch); 173 return pos > -1 && pos % 2 == 1; 174 } 175 176 function charsAround(cm, pos) { 177 var str = cm.getRange(Pos(pos.line, pos.ch - 1), 178 Pos(pos.line, pos.ch + 1)); 179 return str.length == 2 ? str : null; 180 } 181 182 // Project the token type that will exists after the given char is 183 // typed, and use it to determine whether it would cause the start 184 // of a string token. 185 function enteringString(cm, pos, ch) { 186 var line = cm.getLine(pos.line); 187 var token = cm.getTokenAt(pos); 188 if (/\bstring2?\b/.test(token.type)) return false; 189 var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4); 190 stream.pos = stream.start = token.start; 191 for (;;) { 192 var type1 = cm.getMode().token(stream, token.state); 193 if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1); 194 stream.start = stream.pos; 195 } 196 } 197 198 function stringStartsAfter(cm, pos) { 199 var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1)) 200 return /\bstring/.test(token.type) && token.start == pos.ch 201 } 202 }); -
src/wp-includes/js/codemirror/addon/edit/closetag.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 /** 5 * Tag-closer extension for CodeMirror. 6 * 7 * This extension adds an "autoCloseTags" option that can be set to 8 * either true to get the default behavior, or an object to further 9 * configure its behavior. 10 * 11 * These are supported options: 12 * 13 * `whenClosing` (default true) 14 * Whether to autoclose when the '/' of a closing tag is typed. 15 * `whenOpening` (default true) 16 * Whether to autoclose the tag when the final '>' of an opening 17 * tag is typed. 18 * `dontCloseTags` (default is empty tags for HTML, none for XML) 19 * An array of tag names that should not be autoclosed. 20 * `indentTags` (default is block tags for HTML, none for XML) 21 * An array of tag names that should, when opened, cause a 22 * blank line to be added inside the tag, and the blank line and 23 * closing line to be indented. 24 * 25 * See demos/closetag.html for a usage example. 26 */ 27 28 (function(mod) { 29 if (typeof exports == "object" && typeof module == "object") // CommonJS 30 mod(require("../../lib/codemirror"), require("../fold/xml-fold")); 31 else if (typeof define == "function" && define.amd) // AMD 32 define(["../../lib/codemirror", "../fold/xml-fold"], mod); 33 else // Plain browser env 34 mod(CodeMirror); 35 })(function(CodeMirror) { 36 CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { 37 if (old != CodeMirror.Init && old) 38 cm.removeKeyMap("autoCloseTags"); 39 if (!val) return; 40 var map = {name: "autoCloseTags"}; 41 if (typeof val != "object" || val.whenClosing) 42 map["'/'"] = function(cm) { return autoCloseSlash(cm); }; 43 if (typeof val != "object" || val.whenOpening) 44 map["'>'"] = function(cm) { return autoCloseGT(cm); }; 45 cm.addKeyMap(map); 46 }); 47 48 var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", 49 "source", "track", "wbr"]; 50 var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", 51 "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; 52 53 function autoCloseGT(cm) { 54 if (cm.getOption("disableInput")) return CodeMirror.Pass; 55 var ranges = cm.listSelections(), replacements = []; 56 for (var i = 0; i < ranges.length; i++) { 57 if (!ranges[i].empty()) return CodeMirror.Pass; 58 var pos = ranges[i].head, tok = cm.getTokenAt(pos); 59 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; 60 if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; 61 62 var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; 63 var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); 64 var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); 65 66 var tagName = state.tagName; 67 if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); 68 var lowerTagName = tagName.toLowerCase(); 69 // Don't process the '>' at the end of an end-tag or self-closing tag 70 if (!tagName || 71 tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || 72 tok.type == "tag" && state.type == "closeTag" || 73 tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName /> 74 dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || 75 closingTagExists(cm, tagName, pos, state, true)) 76 return CodeMirror.Pass; 77 78 var indent = indentTags && indexOf(indentTags, lowerTagName) > -1; 79 replacements[i] = {indent: indent, 80 text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">", 81 newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)}; 82 } 83 84 for (var i = ranges.length - 1; i >= 0; i--) { 85 var info = replacements[i]; 86 cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert"); 87 var sel = cm.listSelections().slice(0); 88 sel[i] = {head: info.newPos, anchor: info.newPos}; 89 cm.setSelections(sel); 90 if (info.indent) { 91 cm.indentLine(info.newPos.line, null, true); 92 cm.indentLine(info.newPos.line + 1, null, true); 93 } 94 } 95 } 96 97 function autoCloseCurrent(cm, typingSlash) { 98 var ranges = cm.listSelections(), replacements = []; 99 var head = typingSlash ? "/" : "</"; 100 for (var i = 0; i < ranges.length; i++) { 101 if (!ranges[i].empty()) return CodeMirror.Pass; 102 var pos = ranges[i].head, tok = cm.getTokenAt(pos); 103 var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; 104 if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" || 105 tok.start != pos.ch - 1)) 106 return CodeMirror.Pass; 107 // Kludge to get around the fact that we are not in XML mode 108 // when completing in JS/CSS snippet in htmlmixed mode. Does not 109 // work for other XML embedded languages (there is no general 110 // way to go from a mixed mode to its current XML state). 111 var replacement; 112 if (inner.mode.name != "xml") { 113 if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript") 114 replacement = head + "script"; 115 else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css") 116 replacement = head + "style"; 117 else 118 return CodeMirror.Pass; 119 } else { 120 if (!state.context || !state.context.tagName || 121 closingTagExists(cm, state.context.tagName, pos, state)) 122 return CodeMirror.Pass; 123 replacement = head + state.context.tagName; 124 } 125 if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">"; 126 replacements[i] = replacement; 127 } 128 cm.replaceSelections(replacements); 129 ranges = cm.listSelections(); 130 for (var i = 0; i < ranges.length; i++) 131 if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line) 132 cm.indentLine(ranges[i].head.line); 133 } 134 135 function autoCloseSlash(cm) { 136 if (cm.getOption("disableInput")) return CodeMirror.Pass; 137 return autoCloseCurrent(cm, true); 138 } 139 140 CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); }; 141 142 function indexOf(collection, elt) { 143 if (collection.indexOf) return collection.indexOf(elt); 144 for (var i = 0, e = collection.length; i < e; ++i) 145 if (collection[i] == elt) return i; 146 return -1; 147 } 148 149 // If xml-fold is loaded, we use its functionality to try and verify 150 // whether a given tag is actually unclosed. 151 function closingTagExists(cm, tagName, pos, state, newTag) { 152 if (!CodeMirror.scanForClosingTag) return false; 153 var end = Math.min(cm.lastLine() + 1, pos.line + 500); 154 var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end); 155 if (!nextClose || nextClose.tag != tagName) return false; 156 var cx = state.context; 157 // If the immediate wrapping context contains onCx instances of 158 // the same tag, a closing tag only exists if there are at least 159 // that many closing tags of that type following. 160 for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx; 161 pos = nextClose.to; 162 for (var i = 1; i < onCx; i++) { 163 var next = CodeMirror.scanForClosingTag(cm, pos, null, end); 164 if (!next || next.tag != tagName) return false; 165 pos = next.to; 166 } 167 return true; 168 } 169 }); -
src/wp-includes/js/codemirror/addon/edit/continuelist.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var listRE = /^(\s*)(>[> ]*|- \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/, 15 emptyListRE = /^(\s*)(>[> ]*|- \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/, 16 unorderedListRE = /[*+-]\s/; 17 18 CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { 19 if (cm.getOption("disableInput")) return CodeMirror.Pass; 20 var ranges = cm.listSelections(), replacements = []; 21 for (var i = 0; i < ranges.length; i++) { 22 var pos = ranges[i].head; 23 var eolState = cm.getStateAfter(pos.line); 24 var inList = eolState.list !== false; 25 var inQuote = eolState.quote !== 0; 26 27 var line = cm.getLine(pos.line), match = listRE.exec(line); 28 if (!ranges[i].empty() || (!inList && !inQuote) || !match) { 29 cm.execCommand("newlineAndIndent"); 30 return; 31 } 32 if (emptyListRE.test(line)) { 33 cm.replaceRange("", { 34 line: pos.line, ch: 0 35 }, { 36 line: pos.line, ch: pos.ch + 1 37 }); 38 replacements[i] = "\n"; 39 } else { 40 var indent = match[1], after = match[5]; 41 var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0 42 ? match[2].replace("x", " ") 43 : (parseInt(match[3], 10) + 1) + match[4]; 44 45 replacements[i] = "\n" + indent + bullet + after; 46 } 47 } 48 49 cm.replaceSelections(replacements); 50 }; 51 }); -
src/wp-includes/js/codemirror/addon/edit/matchbrackets.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && 13 (document.documentMode == null || document.documentMode < 8); 14 15 var Pos = CodeMirror.Pos; 16 17 var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; 18 19 function findMatchingBracket(cm, where, strict, config) { 20 var line = cm.getLineHandle(where.line), pos = where.ch - 1; 21 var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; 22 if (!match) return null; 23 var dir = match.charAt(1) == ">" ? 1 : -1; 24 if (strict && (dir > 0) != (pos == where.ch)) return null; 25 var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); 26 27 var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); 28 if (found == null) return null; 29 return {from: Pos(where.line, pos), to: found && found.pos, 30 match: found && found.ch == match.charAt(0), forward: dir > 0}; 31 } 32 33 // bracketRegex is used to specify which type of bracket to scan 34 // should be a regexp, e.g. /[[\]]/ 35 // 36 // Note: If "where" is on an open bracket, then this bracket is ignored. 37 // 38 // Returns false when no bracket was found, null when it reached 39 // maxScanLines and gave up 40 function scanForBracket(cm, where, dir, style, config) { 41 var maxScanLen = (config && config.maxScanLineLength) || 10000; 42 var maxScanLines = (config && config.maxScanLines) || 1000; 43 44 var stack = []; 45 var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; 46 var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) 47 : Math.max(cm.firstLine() - 1, where.line - maxScanLines); 48 for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { 49 var line = cm.getLine(lineNo); 50 if (!line) continue; 51 var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; 52 if (line.length > maxScanLen) continue; 53 if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); 54 for (; pos != end; pos += dir) { 55 var ch = line.charAt(pos); 56 if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { 57 var match = matching[ch]; 58 if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch); 59 else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; 60 else stack.pop(); 61 } 62 } 63 } 64 return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; 65 } 66 67 function matchBrackets(cm, autoclear, config) { 68 // Disable brace matching in long lines, since it'll cause hugely slow updates 69 var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; 70 var marks = [], ranges = cm.listSelections(); 71 for (var i = 0; i < ranges.length; i++) { 72 var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config); 73 if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { 74 var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; 75 marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); 76 if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) 77 marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); 78 } 79 } 80 81 if (marks.length) { 82 // Kludge to work around the IE bug from issue #1193, where text 83 // input stops going to the textare whever this fires. 84 if (ie_lt8 && cm.state.focused) cm.focus(); 85 86 var clear = function() { 87 cm.operation(function() { 88 for (var i = 0; i < marks.length; i++) marks[i].clear(); 89 }); 90 }; 91 if (autoclear) setTimeout(clear, 800); 92 else return clear; 93 } 94 } 95 96 var currentlyHighlighted = null; 97 function doMatchBrackets(cm) { 98 cm.operation(function() { 99 if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} 100 currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); 101 }); 102 } 103 104 CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { 105 if (old && old != CodeMirror.Init) { 106 cm.off("cursorActivity", doMatchBrackets); 107 if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} 108 } 109 if (val) { 110 cm.state.matchBrackets = typeof val == "object" ? val : {}; 111 cm.on("cursorActivity", doMatchBrackets); 112 } 113 }); 114 115 CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); 116 CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){ 117 return findMatchingBracket(this, pos, strict, config); 118 }); 119 CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ 120 return scanForBracket(this, pos, dir, style, config); 121 }); 122 }); -
src/wp-includes/js/codemirror/addon/edit/matchtags.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror"), require("../fold/xml-fold")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror", "../fold/xml-fold"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineOption("matchTags", false, function(cm, val, old) { 15 if (old && old != CodeMirror.Init) { 16 cm.off("cursorActivity", doMatchTags); 17 cm.off("viewportChange", maybeUpdateMatch); 18 clear(cm); 19 } 20 if (val) { 21 cm.state.matchBothTags = typeof val == "object" && val.bothTags; 22 cm.on("cursorActivity", doMatchTags); 23 cm.on("viewportChange", maybeUpdateMatch); 24 doMatchTags(cm); 25 } 26 }); 27 28 function clear(cm) { 29 if (cm.state.tagHit) cm.state.tagHit.clear(); 30 if (cm.state.tagOther) cm.state.tagOther.clear(); 31 cm.state.tagHit = cm.state.tagOther = null; 32 } 33 34 function doMatchTags(cm) { 35 cm.state.failedTagMatch = false; 36 cm.operation(function() { 37 clear(cm); 38 if (cm.somethingSelected()) return; 39 var cur = cm.getCursor(), range = cm.getViewport(); 40 range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); 41 var match = CodeMirror.findMatchingTag(cm, cur, range); 42 if (!match) return; 43 if (cm.state.matchBothTags) { 44 var hit = match.at == "open" ? match.open : match.close; 45 if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"}); 46 } 47 var other = match.at == "close" ? match.open : match.close; 48 if (other) 49 cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); 50 else 51 cm.state.failedTagMatch = true; 52 }); 53 } 54 55 function maybeUpdateMatch(cm) { 56 if (cm.state.failedTagMatch) doMatchTags(cm); 57 } 58 59 CodeMirror.commands.toMatchingTag = function(cm) { 60 var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); 61 if (found) { 62 var other = found.at == "close" ? found.open : found.close; 63 if (other) cm.extendSelection(other.to, other.from); 64 } 65 }; 66 }); -
src/wp-includes/js/codemirror/addon/edit/trailingspace.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { 13 if (prev == CodeMirror.Init) prev = false; 14 if (prev && !val) 15 cm.removeOverlay("trailingspace"); 16 else if (!prev && val) 17 cm.addOverlay({ 18 token: function(stream) { 19 for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} 20 if (i > stream.pos) { stream.pos = i; return null; } 21 stream.pos = l; 22 return "trailingspace"; 23 }, 24 name: "trailingspace" 25 }); 26 }); 27 }); -
src/wp-includes/js/codemirror/addon/hint/css-hint.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror"), require("../../mode/css/css")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror", "../../mode/css/css"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1, 15 "first-letter": 1, "first-line": 1, "first-child": 1, 16 before: 1, after: 1, lang: 1}; 17 18 CodeMirror.registerHelper("hint", "css", function(cm) { 19 var cur = cm.getCursor(), token = cm.getTokenAt(cur); 20 var inner = CodeMirror.innerMode(cm.getMode(), token.state); 21 if (inner.mode.name != "css") return; 22 23 if (token.type == "keyword" && "!important".indexOf(token.string) == 0) 24 return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start), 25 to: CodeMirror.Pos(cur.line, token.end)}; 26 27 var start = token.start, end = cur.ch, word = token.string.slice(0, end - start); 28 if (/[^\w$_-]/.test(word)) { 29 word = ""; start = end = cur.ch; 30 } 31 32 var spec = CodeMirror.resolveMode("text/css"); 33 34 var result = []; 35 function add(keywords) { 36 for (var name in keywords) 37 if (!word || name.lastIndexOf(word, 0) == 0) 38 result.push(name); 39 } 40 41 var st = inner.state.state; 42 if (st == "pseudo" || token.type == "variable-3") { 43 add(pseudoClasses); 44 } else if (st == "block" || st == "maybeprop") { 45 add(spec.propertyKeywords); 46 } else if (st == "prop" || st == "parens" || st == "at" || st == "params") { 47 add(spec.valueKeywords); 48 add(spec.colorKeywords); 49 } else if (st == "media" || st == "media_parens") { 50 add(spec.mediaTypes); 51 add(spec.mediaFeatures); 52 } 53 54 if (result.length) return { 55 list: result, 56 from: CodeMirror.Pos(cur.line, start), 57 to: CodeMirror.Pos(cur.line, end) 58 }; 59 }); 60 }); -
src/wp-includes/js/codemirror/addon/hint/html-hint.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror"), require("./xml-hint")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror", "./xml-hint"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" "); 15 var targets = ["_blank", "_self", "_top", "_parent"]; 16 var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"]; 17 var methods = ["get", "post", "put", "delete"]; 18 var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]; 19 var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech", 20 "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait", 21 "orientation:landscape", "device-height: [X]", "device-width: [X]"]; 22 var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags 23 24 var data = { 25 a: { 26 attrs: { 27 href: null, ping: null, type: null, 28 media: media, 29 target: targets, 30 hreflang: langs 31 } 32 }, 33 abbr: s, 34 acronym: s, 35 address: s, 36 applet: s, 37 area: { 38 attrs: { 39 alt: null, coords: null, href: null, target: null, ping: null, 40 media: media, hreflang: langs, type: null, 41 shape: ["default", "rect", "circle", "poly"] 42 } 43 }, 44 article: s, 45 aside: s, 46 audio: { 47 attrs: { 48 src: null, mediagroup: null, 49 crossorigin: ["anonymous", "use-credentials"], 50 preload: ["none", "metadata", "auto"], 51 autoplay: ["", "autoplay"], 52 loop: ["", "loop"], 53 controls: ["", "controls"] 54 } 55 }, 56 b: s, 57 base: { attrs: { href: null, target: targets } }, 58 basefont: s, 59 bdi: s, 60 bdo: s, 61 big: s, 62 blockquote: { attrs: { cite: null } }, 63 body: s, 64 br: s, 65 button: { 66 attrs: { 67 form: null, formaction: null, name: null, value: null, 68 autofocus: ["", "autofocus"], 69 disabled: ["", "autofocus"], 70 formenctype: encs, 71 formmethod: methods, 72 formnovalidate: ["", "novalidate"], 73 formtarget: targets, 74 type: ["submit", "reset", "button"] 75 } 76 }, 77 canvas: { attrs: { width: null, height: null } }, 78 caption: s, 79 center: s, 80 cite: s, 81 code: s, 82 col: { attrs: { span: null } }, 83 colgroup: { attrs: { span: null } }, 84 command: { 85 attrs: { 86 type: ["command", "checkbox", "radio"], 87 label: null, icon: null, radiogroup: null, command: null, title: null, 88 disabled: ["", "disabled"], 89 checked: ["", "checked"] 90 } 91 }, 92 data: { attrs: { value: null } }, 93 datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } }, 94 datalist: { attrs: { data: null } }, 95 dd: s, 96 del: { attrs: { cite: null, datetime: null } }, 97 details: { attrs: { open: ["", "open"] } }, 98 dfn: s, 99 dir: s, 100 div: s, 101 dl: s, 102 dt: s, 103 em: s, 104 embed: { attrs: { src: null, type: null, width: null, height: null } }, 105 eventsource: { attrs: { src: null } }, 106 fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } }, 107 figcaption: s, 108 figure: s, 109 font: s, 110 footer: s, 111 form: { 112 attrs: { 113 action: null, name: null, 114 "accept-charset": charsets, 115 autocomplete: ["on", "off"], 116 enctype: encs, 117 method: methods, 118 novalidate: ["", "novalidate"], 119 target: targets 120 } 121 }, 122 frame: s, 123 frameset: s, 124 h1: s, h2: s, h3: s, h4: s, h5: s, h6: s, 125 head: { 126 attrs: {}, 127 children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"] 128 }, 129 header: s, 130 hgroup: s, 131 hr: s, 132 html: { 133 attrs: { manifest: null }, 134 children: ["head", "body"] 135 }, 136 i: s, 137 iframe: { 138 attrs: { 139 src: null, srcdoc: null, name: null, width: null, height: null, 140 sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"], 141 seamless: ["", "seamless"] 142 } 143 }, 144 img: { 145 attrs: { 146 alt: null, src: null, ismap: null, usemap: null, width: null, height: null, 147 crossorigin: ["anonymous", "use-credentials"] 148 } 149 }, 150 input: { 151 attrs: { 152 alt: null, dirname: null, form: null, formaction: null, 153 height: null, list: null, max: null, maxlength: null, min: null, 154 name: null, pattern: null, placeholder: null, size: null, src: null, 155 step: null, value: null, width: null, 156 accept: ["audio/*", "video/*", "image/*"], 157 autocomplete: ["on", "off"], 158 autofocus: ["", "autofocus"], 159 checked: ["", "checked"], 160 disabled: ["", "disabled"], 161 formenctype: encs, 162 formmethod: methods, 163 formnovalidate: ["", "novalidate"], 164 formtarget: targets, 165 multiple: ["", "multiple"], 166 readonly: ["", "readonly"], 167 required: ["", "required"], 168 type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month", 169 "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio", 170 "file", "submit", "image", "reset", "button"] 171 } 172 }, 173 ins: { attrs: { cite: null, datetime: null } }, 174 kbd: s, 175 keygen: { 176 attrs: { 177 challenge: null, form: null, name: null, 178 autofocus: ["", "autofocus"], 179 disabled: ["", "disabled"], 180 keytype: ["RSA"] 181 } 182 }, 183 label: { attrs: { "for": null, form: null } }, 184 legend: s, 185 li: { attrs: { value: null } }, 186 link: { 187 attrs: { 188 href: null, type: null, 189 hreflang: langs, 190 media: media, 191 sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"] 192 } 193 }, 194 map: { attrs: { name: null } }, 195 mark: s, 196 menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } }, 197 meta: { 198 attrs: { 199 content: null, 200 charset: charsets, 201 name: ["viewport", "application-name", "author", "description", "generator", "keywords"], 202 "http-equiv": ["content-language", "content-type", "default-style", "refresh"] 203 } 204 }, 205 meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } }, 206 nav: s, 207 noframes: s, 208 noscript: s, 209 object: { 210 attrs: { 211 data: null, type: null, name: null, usemap: null, form: null, width: null, height: null, 212 typemustmatch: ["", "typemustmatch"] 213 } 214 }, 215 ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } }, 216 optgroup: { attrs: { disabled: ["", "disabled"], label: null } }, 217 option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } }, 218 output: { attrs: { "for": null, form: null, name: null } }, 219 p: s, 220 param: { attrs: { name: null, value: null } }, 221 pre: s, 222 progress: { attrs: { value: null, max: null } }, 223 q: { attrs: { cite: null } }, 224 rp: s, 225 rt: s, 226 ruby: s, 227 s: s, 228 samp: s, 229 script: { 230 attrs: { 231 type: ["text/javascript"], 232 src: null, 233 async: ["", "async"], 234 defer: ["", "defer"], 235 charset: charsets 236 } 237 }, 238 section: s, 239 select: { 240 attrs: { 241 form: null, name: null, size: null, 242 autofocus: ["", "autofocus"], 243 disabled: ["", "disabled"], 244 multiple: ["", "multiple"] 245 } 246 }, 247 small: s, 248 source: { attrs: { src: null, type: null, media: null } }, 249 span: s, 250 strike: s, 251 strong: s, 252 style: { 253 attrs: { 254 type: ["text/css"], 255 media: media, 256 scoped: null 257 } 258 }, 259 sub: s, 260 summary: s, 261 sup: s, 262 table: s, 263 tbody: s, 264 td: { attrs: { colspan: null, rowspan: null, headers: null } }, 265 textarea: { 266 attrs: { 267 dirname: null, form: null, maxlength: null, name: null, placeholder: null, 268 rows: null, cols: null, 269 autofocus: ["", "autofocus"], 270 disabled: ["", "disabled"], 271 readonly: ["", "readonly"], 272 required: ["", "required"], 273 wrap: ["soft", "hard"] 274 } 275 }, 276 tfoot: s, 277 th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } }, 278 thead: s, 279 time: { attrs: { datetime: null } }, 280 title: s, 281 tr: s, 282 track: { 283 attrs: { 284 src: null, label: null, "default": null, 285 kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"], 286 srclang: langs 287 } 288 }, 289 tt: s, 290 u: s, 291 ul: s, 292 "var": s, 293 video: { 294 attrs: { 295 src: null, poster: null, width: null, height: null, 296 crossorigin: ["anonymous", "use-credentials"], 297 preload: ["auto", "metadata", "none"], 298 autoplay: ["", "autoplay"], 299 mediagroup: ["movie"], 300 muted: ["", "muted"], 301 controls: ["", "controls"] 302 } 303 }, 304 wbr: s 305 }; 306 307 var globalAttrs = { 308 accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], 309 "class": null, 310 contenteditable: ["true", "false"], 311 contextmenu: null, 312 dir: ["ltr", "rtl", "auto"], 313 draggable: ["true", "false", "auto"], 314 dropzone: ["copy", "move", "link", "string:", "file:"], 315 hidden: ["hidden"], 316 id: null, 317 inert: ["inert"], 318 itemid: null, 319 itemprop: null, 320 itemref: null, 321 itemscope: ["itemscope"], 322 itemtype: null, 323 lang: ["en", "es"], 324 spellcheck: ["true", "false"], 325 style: null, 326 tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"], 327 title: null, 328 translate: ["yes", "no"], 329 onclick: null, 330 rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"] 331 }; 332 function populate(obj) { 333 for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr)) 334 obj.attrs[attr] = globalAttrs[attr]; 335 } 336 337 populate(s); 338 for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s) 339 populate(data[tag]); 340 341 CodeMirror.htmlSchema = data; 342 function htmlHint(cm, options) { 343 var local = {schemaInfo: data}; 344 if (options) for (var opt in options) local[opt] = options[opt]; 345 return CodeMirror.hint.xml(cm, local); 346 } 347 CodeMirror.registerHelper("hint", "html", htmlHint); 348 }); -
src/wp-includes/js/codemirror/addon/hint/javascript-hint.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 var Pos = CodeMirror.Pos; 13 14 function forEach(arr, f) { 15 for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); 16 } 17 18 function arrayContains(arr, item) { 19 if (!Array.prototype.indexOf) { 20 var i = arr.length; 21 while (i--) { 22 if (arr[i] === item) { 23 return true; 24 } 25 } 26 return false; 27 } 28 return arr.indexOf(item) != -1; 29 } 30 31 function scriptHint(editor, keywords, getToken, options) { 32 // Find the token at the cursor 33 var cur = editor.getCursor(), token = getToken(editor, cur); 34 if (/\b(?:string|comment)\b/.test(token.type)) return; 35 token.state = CodeMirror.innerMode(editor.getMode(), token.state).state; 36 37 // If it's not a 'word-style' token, ignore the token. 38 if (!/^[\w$_]*$/.test(token.string)) { 39 token = {start: cur.ch, end: cur.ch, string: "", state: token.state, 40 type: token.string == "." ? "property" : null}; 41 } else if (token.end > cur.ch) { 42 token.end = cur.ch; 43 token.string = token.string.slice(0, cur.ch - token.start); 44 } 45 46 var tprop = token; 47 // If it is a property, find out what it is a property of. 48 while (tprop.type == "property") { 49 tprop = getToken(editor, Pos(cur.line, tprop.start)); 50 if (tprop.string != ".") return; 51 tprop = getToken(editor, Pos(cur.line, tprop.start)); 52 if (!context) var context = []; 53 context.push(tprop); 54 } 55 return {list: getCompletions(token, context, keywords, options), 56 from: Pos(cur.line, token.start), 57 to: Pos(cur.line, token.end)}; 58 } 59 60 function javascriptHint(editor, options) { 61 return scriptHint(editor, javascriptKeywords, 62 function (e, cur) {return e.getTokenAt(cur);}, 63 options); 64 }; 65 CodeMirror.registerHelper("hint", "javascript", javascriptHint); 66 67 function getCoffeeScriptToken(editor, cur) { 68 // This getToken, it is for coffeescript, imitates the behavior of 69 // getTokenAt method in javascript.js, that is, returning "property" 70 // type and treat "." as indepenent token. 71 var token = editor.getTokenAt(cur); 72 if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') { 73 token.end = token.start; 74 token.string = '.'; 75 token.type = "property"; 76 } 77 else if (/^\.[\w$_]*$/.test(token.string)) { 78 token.type = "property"; 79 token.start++; 80 token.string = token.string.replace(/\./, ''); 81 } 82 return token; 83 } 84 85 function coffeescriptHint(editor, options) { 86 return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); 87 } 88 CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint); 89 90 var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + 91 "toUpperCase toLowerCase split concat match replace search").split(" "); 92 var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " + 93 "lastIndexOf every some filter forEach map reduce reduceRight ").split(" "); 94 var funcProps = "prototype apply call bind".split(" "); 95 var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " + 96 "if in instanceof new null return switch throw true try typeof var void while with").split(" "); 97 var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " + 98 "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" "); 99 100 function forAllProps(obj, callback) { 101 if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) { 102 for (var name in obj) callback(name) 103 } else { 104 for (var o = obj; o; o = Object.getPrototypeOf(o)) 105 Object.getOwnPropertyNames(o).forEach(callback) 106 } 107 } 108 109 function getCompletions(token, context, keywords, options) { 110 var found = [], start = token.string, global = options && options.globalScope || window; 111 function maybeAdd(str) { 112 if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); 113 } 114 function gatherCompletions(obj) { 115 if (typeof obj == "string") forEach(stringProps, maybeAdd); 116 else if (obj instanceof Array) forEach(arrayProps, maybeAdd); 117 else if (obj instanceof Function) forEach(funcProps, maybeAdd); 118 forAllProps(obj, maybeAdd) 119 } 120 121 if (context && context.length) { 122 // If this is a property, see if it belongs to some object we can 123 // find in the current environment. 124 var obj = context.pop(), base; 125 if (obj.type && obj.type.indexOf("variable") === 0) { 126 if (options && options.additionalContext) 127 base = options.additionalContext[obj.string]; 128 if (!options || options.useGlobalScope !== false) 129 base = base || global[obj.string]; 130 } else if (obj.type == "string") { 131 base = ""; 132 } else if (obj.type == "atom") { 133 base = 1; 134 } else if (obj.type == "function") { 135 if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && 136 (typeof global.jQuery == 'function')) 137 base = global.jQuery(); 138 else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function')) 139 base = global._(); 140 } 141 while (base != null && context.length) 142 base = base[context.pop().string]; 143 if (base != null) gatherCompletions(base); 144 } else { 145 // If not, just look in the global object and any local scope 146 // (reading into JS mode internals to get at the local and global variables) 147 for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); 148 for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name); 149 if (!options || options.useGlobalScope !== false) 150 gatherCompletions(global); 151 forEach(keywords, maybeAdd); 152 } 153 return found; 154 } 155 }); -
src/wp-includes/js/codemirror/addon/hint/show-hint.css
1 .CodeMirror-hints { 2 position: absolute; 3 z-index: 10; 4 overflow: hidden; 5 list-style: none; 6 7 margin: 0; 8 padding: 2px; 9 10 -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 border-radius: 3px; 14 border: 1px solid silver; 15 16 background: white; 17 font-size: 90%; 18 font-family: monospace; 19 20 max-height: 20em; 21 overflow-y: auto; 22 } 23 24 .CodeMirror-hint { 25 margin: 0; 26 padding: 0 4px; 27 border-radius: 2px; 28 white-space: pre; 29 color: black; 30 cursor: pointer; 31 } 32 33 li.CodeMirror-hint-active { 34 background: #08f; 35 color: white; 36 } -
src/wp-includes/js/codemirror/addon/hint/show-hint.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var HINT_ELEMENT_CLASS = "CodeMirror-hint"; 15 var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; 16 17 // This is the old interface, kept around for now to stay 18 // backwards-compatible. 19 CodeMirror.showHint = function(cm, getHints, options) { 20 if (!getHints) return cm.showHint(options); 21 if (options && options.async) getHints.async = true; 22 var newOpts = {hint: getHints}; 23 if (options) for (var prop in options) newOpts[prop] = options[prop]; 24 return cm.showHint(newOpts); 25 }; 26 27 CodeMirror.defineExtension("showHint", function(options) { 28 options = parseOptions(this, this.getCursor("start"), options); 29 var selections = this.listSelections() 30 if (selections.length > 1) return; 31 // By default, don't allow completion when something is selected. 32 // A hint function can have a `supportsSelection` property to 33 // indicate that it can handle selections. 34 if (this.somethingSelected()) { 35 if (!options.hint.supportsSelection) return; 36 // Don't try with cross-line selections 37 for (var i = 0; i < selections.length; i++) 38 if (selections[i].head.line != selections[i].anchor.line) return; 39 } 40 41 if (this.state.completionActive) this.state.completionActive.close(); 42 var completion = this.state.completionActive = new Completion(this, options); 43 if (!completion.options.hint) return; 44 45 CodeMirror.signal(this, "startCompletion", this); 46 completion.update(true); 47 }); 48 49 function Completion(cm, options) { 50 this.cm = cm; 51 this.options = options; 52 this.widget = null; 53 this.debounce = 0; 54 this.tick = 0; 55 this.startPos = this.cm.getCursor("start"); 56 this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; 57 58 var self = this; 59 cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); 60 } 61 62 var requestAnimationFrame = window.requestAnimationFrame || function(fn) { 63 return setTimeout(fn, 1000/60); 64 }; 65 var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; 66 67 Completion.prototype = { 68 close: function() { 69 if (!this.active()) return; 70 this.cm.state.completionActive = null; 71 this.tick = null; 72 this.cm.off("cursorActivity", this.activityFunc); 73 74 if (this.widget && this.data) CodeMirror.signal(this.data, "close"); 75 if (this.widget) this.widget.close(); 76 CodeMirror.signal(this.cm, "endCompletion", this.cm); 77 }, 78 79 active: function() { 80 return this.cm.state.completionActive == this; 81 }, 82 83 pick: function(data, i) { 84 var completion = data.list[i]; 85 if (completion.hint) completion.hint(this.cm, data, completion); 86 else this.cm.replaceRange(getText(completion), completion.from || data.from, 87 completion.to || data.to, "complete"); 88 CodeMirror.signal(data, "pick", completion); 89 this.close(); 90 }, 91 92 cursorActivity: function() { 93 if (this.debounce) { 94 cancelAnimationFrame(this.debounce); 95 this.debounce = 0; 96 } 97 98 var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); 99 if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || 100 pos.ch < this.startPos.ch || this.cm.somethingSelected() || 101 (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { 102 this.close(); 103 } else { 104 var self = this; 105 this.debounce = requestAnimationFrame(function() {self.update();}); 106 if (this.widget) this.widget.disable(); 107 } 108 }, 109 110 update: function(first) { 111 if (this.tick == null) return 112 var self = this, myTick = ++this.tick 113 fetchHints(this.options.hint, this.cm, this.options, function(data) { 114 if (self.tick == myTick) self.finishUpdate(data, first) 115 }) 116 }, 117 118 finishUpdate: function(data, first) { 119 if (this.data) CodeMirror.signal(this.data, "update"); 120 121 var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); 122 if (this.widget) this.widget.close(); 123 124 if (data && this.data && isNewCompletion(this.data, data)) return; 125 this.data = data; 126 127 if (data && data.list.length) { 128 if (picked && data.list.length == 1) { 129 this.pick(data, 0); 130 } else { 131 this.widget = new Widget(this, data); 132 CodeMirror.signal(data, "shown"); 133 } 134 } 135 } 136 }; 137 138 function isNewCompletion(old, nw) { 139 var moved = CodeMirror.cmpPos(nw.from, old.from) 140 return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch 141 } 142 143 function parseOptions(cm, pos, options) { 144 var editor = cm.options.hintOptions; 145 var out = {}; 146 for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; 147 if (editor) for (var prop in editor) 148 if (editor[prop] !== undefined) out[prop] = editor[prop]; 149 if (options) for (var prop in options) 150 if (options[prop] !== undefined) out[prop] = options[prop]; 151 if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos) 152 return out; 153 } 154 155 function getText(completion) { 156 if (typeof completion == "string") return completion; 157 else return completion.text; 158 } 159 160 function buildKeyMap(completion, handle) { 161 var baseMap = { 162 Up: function() {handle.moveFocus(-1);}, 163 Down: function() {handle.moveFocus(1);}, 164 PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, 165 PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, 166 Home: function() {handle.setFocus(0);}, 167 End: function() {handle.setFocus(handle.length - 1);}, 168 Enter: handle.pick, 169 Tab: handle.pick, 170 Esc: handle.close 171 }; 172 var custom = completion.options.customKeys; 173 var ourMap = custom ? {} : baseMap; 174 function addBinding(key, val) { 175 var bound; 176 if (typeof val != "string") 177 bound = function(cm) { return val(cm, handle); }; 178 // This mechanism is deprecated 179 else if (baseMap.hasOwnProperty(val)) 180 bound = baseMap[val]; 181 else 182 bound = val; 183 ourMap[key] = bound; 184 } 185 if (custom) 186 for (var key in custom) if (custom.hasOwnProperty(key)) 187 addBinding(key, custom[key]); 188 var extra = completion.options.extraKeys; 189 if (extra) 190 for (var key in extra) if (extra.hasOwnProperty(key)) 191 addBinding(key, extra[key]); 192 return ourMap; 193 } 194 195 function getHintElement(hintsElement, el) { 196 while (el && el != hintsElement) { 197 if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; 198 el = el.parentNode; 199 } 200 } 201 202 function Widget(completion, data) { 203 this.completion = completion; 204 this.data = data; 205 this.picked = false; 206 var widget = this, cm = completion.cm; 207 208 var hints = this.hints = document.createElement("ul"); 209 hints.className = "CodeMirror-hints"; 210 this.selectedHint = data.selectedHint || 0; 211 212 var completions = data.list; 213 for (var i = 0; i < completions.length; ++i) { 214 var elt = hints.appendChild(document.createElement("li")), cur = completions[i]; 215 var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); 216 if (cur.className != null) className = cur.className + " " + className; 217 elt.className = className; 218 if (cur.render) cur.render(elt, data, cur); 219 else elt.appendChild(document.createTextNode(cur.displayText || getText(cur))); 220 elt.hintId = i; 221 } 222 223 var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); 224 var left = pos.left, top = pos.bottom, below = true; 225 hints.style.left = left + "px"; 226 hints.style.top = top + "px"; 227 // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. 228 var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); 229 var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); 230 (completion.options.container || document.body).appendChild(hints); 231 var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; 232 var scrolls = hints.scrollHeight > hints.clientHeight + 1 233 var startScroll = cm.getScrollInfo(); 234 235 if (overlapY > 0) { 236 var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); 237 if (curTop - height > 0) { // Fits above cursor 238 hints.style.top = (top = pos.top - height) + "px"; 239 below = false; 240 } else if (height > winH) { 241 hints.style.height = (winH - 5) + "px"; 242 hints.style.top = (top = pos.bottom - box.top) + "px"; 243 var cursor = cm.getCursor(); 244 if (data.from.ch != cursor.ch) { 245 pos = cm.cursorCoords(cursor); 246 hints.style.left = (left = pos.left) + "px"; 247 box = hints.getBoundingClientRect(); 248 } 249 } 250 } 251 var overlapX = box.right - winW; 252 if (overlapX > 0) { 253 if (box.right - box.left > winW) { 254 hints.style.width = (winW - 5) + "px"; 255 overlapX -= (box.right - box.left) - winW; 256 } 257 hints.style.left = (left = pos.left - overlapX) + "px"; 258 } 259 if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) 260 node.style.paddingRight = cm.display.nativeBarWidth + "px" 261 262 cm.addKeyMap(this.keyMap = buildKeyMap(completion, { 263 moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, 264 setFocus: function(n) { widget.changeActive(n); }, 265 menuSize: function() { return widget.screenAmount(); }, 266 length: completions.length, 267 close: function() { completion.close(); }, 268 pick: function() { widget.pick(); }, 269 data: data 270 })); 271 272 if (completion.options.closeOnUnfocus) { 273 var closingOnBlur; 274 cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); 275 cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); 276 } 277 278 cm.on("scroll", this.onScroll = function() { 279 var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); 280 var newTop = top + startScroll.top - curScroll.top; 281 var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop); 282 if (!below) point += hints.offsetHeight; 283 if (point <= editor.top || point >= editor.bottom) return completion.close(); 284 hints.style.top = newTop + "px"; 285 hints.style.left = (left + startScroll.left - curScroll.left) + "px"; 286 }); 287 288 CodeMirror.on(hints, "dblclick", function(e) { 289 var t = getHintElement(hints, e.target || e.srcElement); 290 if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} 291 }); 292 293 CodeMirror.on(hints, "click", function(e) { 294 var t = getHintElement(hints, e.target || e.srcElement); 295 if (t && t.hintId != null) { 296 widget.changeActive(t.hintId); 297 if (completion.options.completeOnSingleClick) widget.pick(); 298 } 299 }); 300 301 CodeMirror.on(hints, "mousedown", function() { 302 setTimeout(function(){cm.focus();}, 20); 303 }); 304 305 CodeMirror.signal(data, "select", completions[0], hints.firstChild); 306 return true; 307 } 308 309 Widget.prototype = { 310 close: function() { 311 if (this.completion.widget != this) return; 312 this.completion.widget = null; 313 this.hints.parentNode.removeChild(this.hints); 314 this.completion.cm.removeKeyMap(this.keyMap); 315 316 var cm = this.completion.cm; 317 if (this.completion.options.closeOnUnfocus) { 318 cm.off("blur", this.onBlur); 319 cm.off("focus", this.onFocus); 320 } 321 cm.off("scroll", this.onScroll); 322 }, 323 324 disable: function() { 325 this.completion.cm.removeKeyMap(this.keyMap); 326 var widget = this; 327 this.keyMap = {Enter: function() { widget.picked = true; }}; 328 this.completion.cm.addKeyMap(this.keyMap); 329 }, 330 331 pick: function() { 332 this.completion.pick(this.data, this.selectedHint); 333 }, 334 335 changeActive: function(i, avoidWrap) { 336 if (i >= this.data.list.length) 337 i = avoidWrap ? this.data.list.length - 1 : 0; 338 else if (i < 0) 339 i = avoidWrap ? 0 : this.data.list.length - 1; 340 if (this.selectedHint == i) return; 341 var node = this.hints.childNodes[this.selectedHint]; 342 node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); 343 node = this.hints.childNodes[this.selectedHint = i]; 344 node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; 345 if (node.offsetTop < this.hints.scrollTop) 346 this.hints.scrollTop = node.offsetTop - 3; 347 else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) 348 this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; 349 CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); 350 }, 351 352 screenAmount: function() { 353 return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; 354 } 355 }; 356 357 function applicableHelpers(cm, helpers) { 358 if (!cm.somethingSelected()) return helpers 359 var result = [] 360 for (var i = 0; i < helpers.length; i++) 361 if (helpers[i].supportsSelection) result.push(helpers[i]) 362 return result 363 } 364 365 function fetchHints(hint, cm, options, callback) { 366 if (hint.async) { 367 hint(cm, callback, options) 368 } else { 369 var result = hint(cm, options) 370 if (result && result.then) result.then(callback) 371 else callback(result) 372 } 373 } 374 375 function resolveAutoHints(cm, pos) { 376 var helpers = cm.getHelpers(pos, "hint"), words 377 if (helpers.length) { 378 var resolved = function(cm, callback, options) { 379 var app = applicableHelpers(cm, helpers); 380 function run(i) { 381 if (i == app.length) return callback(null) 382 fetchHints(app[i], cm, options, function(result) { 383 if (result && result.list.length > 0) callback(result) 384 else run(i + 1) 385 }) 386 } 387 run(0) 388 } 389 resolved.async = true 390 resolved.supportsSelection = true 391 return resolved 392 } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { 393 return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) } 394 } else if (CodeMirror.hint.anyword) { 395 return function(cm, options) { return CodeMirror.hint.anyword(cm, options) } 396 } else { 397 return function() {} 398 } 399 } 400 401 CodeMirror.registerHelper("hint", "auto", { 402 resolve: resolveAutoHints 403 }); 404 405 CodeMirror.registerHelper("hint", "fromList", function(cm, options) { 406 var cur = cm.getCursor(), token = cm.getTokenAt(cur); 407 var to = CodeMirror.Pos(cur.line, token.end); 408 if (token.string && /\w/.test(token.string[token.string.length - 1])) { 409 var term = token.string, from = CodeMirror.Pos(cur.line, token.start); 410 } else { 411 var term = "", from = to; 412 } 413 var found = []; 414 for (var i = 0; i < options.words.length; i++) { 415 var word = options.words[i]; 416 if (word.slice(0, term.length) == term) 417 found.push(word); 418 } 419 420 if (found.length) return {list: found, from: from, to: to}; 421 }); 422 423 CodeMirror.commands.autocomplete = CodeMirror.showHint; 424 425 var defaultOptions = { 426 hint: CodeMirror.hint.auto, 427 completeSingle: true, 428 alignWithWord: true, 429 closeCharacters: /[\s()\[\]{};:>,]/, 430 closeOnUnfocus: true, 431 completeOnSingleClick: true, 432 container: null, 433 customKeys: null, 434 extraKeys: null 435 }; 436 437 CodeMirror.defineOption("hintOptions", null); 438 }); -
src/wp-includes/js/codemirror/addon/hint/sql-hint.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror"), require("../../mode/sql/sql")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror", "../../mode/sql/sql"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var tables; 15 var defaultTable; 16 var keywords; 17 var CONS = { 18 QUERY_DIV: ";", 19 ALIAS_KEYWORD: "AS" 20 }; 21 var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos; 22 23 function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" } 24 25 function getKeywords(editor) { 26 var mode = editor.doc.modeOption; 27 if (mode === "sql") mode = "text/x-sql"; 28 return CodeMirror.resolveMode(mode).keywords; 29 } 30 31 function getText(item) { 32 return typeof item == "string" ? item : item.text; 33 } 34 35 function wrapTable(name, value) { 36 if (isArray(value)) value = {columns: value} 37 if (!value.text) value.text = name 38 return value 39 } 40 41 function parseTables(input) { 42 var result = {} 43 if (isArray(input)) { 44 for (var i = input.length - 1; i >= 0; i--) { 45 var item = input[i] 46 result[getText(item).toUpperCase()] = wrapTable(getText(item), item) 47 } 48 } else if (input) { 49 for (var name in input) 50 result[name.toUpperCase()] = wrapTable(name, input[name]) 51 } 52 return result 53 } 54 55 function getTable(name) { 56 return tables[name.toUpperCase()] 57 } 58 59 function shallowClone(object) { 60 var result = {}; 61 for (var key in object) if (object.hasOwnProperty(key)) 62 result[key] = object[key]; 63 return result; 64 } 65 66 function match(string, word) { 67 var len = string.length; 68 var sub = getText(word).substr(0, len); 69 return string.toUpperCase() === sub.toUpperCase(); 70 } 71 72 function addMatches(result, search, wordlist, formatter) { 73 if (isArray(wordlist)) { 74 for (var i = 0; i < wordlist.length; i++) 75 if (match(search, wordlist[i])) result.push(formatter(wordlist[i])) 76 } else { 77 for (var word in wordlist) if (wordlist.hasOwnProperty(word)) { 78 var val = wordlist[word] 79 if (!val || val === true) 80 val = word 81 else 82 val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text 83 if (match(search, val)) result.push(formatter(val)) 84 } 85 } 86 } 87 88 function cleanName(name) { 89 // Get rid name from backticks(`) and preceding dot(.) 90 if (name.charAt(0) == ".") { 91 name = name.substr(1); 92 } 93 return name.replace(/`/g, ""); 94 } 95 96 function insertBackticks(name) { 97 var nameParts = getText(name).split("."); 98 for (var i = 0; i < nameParts.length; i++) 99 nameParts[i] = "`" + nameParts[i] + "`"; 100 var escaped = nameParts.join("."); 101 if (typeof name == "string") return escaped; 102 name = shallowClone(name); 103 name.text = escaped; 104 return name; 105 } 106 107 function nameCompletion(cur, token, result, editor) { 108 // Try to complete table, column names and return start position of completion 109 var useBacktick = false; 110 var nameParts = []; 111 var start = token.start; 112 var cont = true; 113 while (cont) { 114 cont = (token.string.charAt(0) == "."); 115 useBacktick = useBacktick || (token.string.charAt(0) == "`"); 116 117 start = token.start; 118 nameParts.unshift(cleanName(token.string)); 119 120 token = editor.getTokenAt(Pos(cur.line, token.start)); 121 if (token.string == ".") { 122 cont = true; 123 token = editor.getTokenAt(Pos(cur.line, token.start)); 124 } 125 } 126 127 // Try to complete table names 128 var string = nameParts.join("."); 129 addMatches(result, string, tables, function(w) { 130 return useBacktick ? insertBackticks(w) : w; 131 }); 132 133 // Try to complete columns from defaultTable 134 addMatches(result, string, defaultTable, function(w) { 135 return useBacktick ? insertBackticks(w) : w; 136 }); 137 138 // Try to complete columns 139 string = nameParts.pop(); 140 var table = nameParts.join("."); 141 142 var alias = false; 143 var aliasTable = table; 144 // Check if table is available. If not, find table by Alias 145 if (!getTable(table)) { 146 var oldTable = table; 147 table = findTableByAlias(table, editor); 148 if (table !== oldTable) alias = true; 149 } 150 151 var columns = getTable(table); 152 if (columns && columns.columns) 153 columns = columns.columns; 154 155 if (columns) { 156 addMatches(result, string, columns, function(w) { 157 var tableInsert = table; 158 if (alias == true) tableInsert = aliasTable; 159 if (typeof w == "string") { 160 w = tableInsert + "." + w; 161 } else { 162 w = shallowClone(w); 163 w.text = tableInsert + "." + w.text; 164 } 165 return useBacktick ? insertBackticks(w) : w; 166 }); 167 } 168 169 return start; 170 } 171 172 function eachWord(lineText, f) { 173 if (!lineText) return; 174 var excepted = /[,;]/g; 175 var words = lineText.split(" "); 176 for (var i = 0; i < words.length; i++) { 177 f(words[i]?words[i].replace(excepted, '') : ''); 178 } 179 } 180 181 function findTableByAlias(alias, editor) { 182 var doc = editor.doc; 183 var fullQuery = doc.getValue(); 184 var aliasUpperCase = alias.toUpperCase(); 185 var previousWord = ""; 186 var table = ""; 187 var separator = []; 188 var validRange = { 189 start: Pos(0, 0), 190 end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length) 191 }; 192 193 //add separator 194 var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV); 195 while(indexOfSeparator != -1) { 196 separator.push(doc.posFromIndex(indexOfSeparator)); 197 indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1); 198 } 199 separator.unshift(Pos(0, 0)); 200 separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length)); 201 202 //find valid range 203 var prevItem = null; 204 var current = editor.getCursor() 205 for (var i = 0; i < separator.length; i++) { 206 if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) { 207 validRange = {start: prevItem, end: separator[i]}; 208 break; 209 } 210 prevItem = separator[i]; 211 } 212 213 var query = doc.getRange(validRange.start, validRange.end, false); 214 215 for (var i = 0; i < query.length; i++) { 216 var lineText = query[i]; 217 eachWord(lineText, function(word) { 218 var wordUpperCase = word.toUpperCase(); 219 if (wordUpperCase === aliasUpperCase && getTable(previousWord)) 220 table = previousWord; 221 if (wordUpperCase !== CONS.ALIAS_KEYWORD) 222 previousWord = word; 223 }); 224 if (table) break; 225 } 226 return table; 227 } 228 229 CodeMirror.registerHelper("hint", "sql", function(editor, options) { 230 tables = parseTables(options && options.tables) 231 var defaultTableName = options && options.defaultTable; 232 var disableKeywords = options && options.disableKeywords; 233 defaultTable = defaultTableName && getTable(defaultTableName); 234 keywords = getKeywords(editor); 235 236 if (defaultTableName && !defaultTable) 237 defaultTable = findTableByAlias(defaultTableName, editor); 238 239 defaultTable = defaultTable || []; 240 241 if (defaultTable.columns) 242 defaultTable = defaultTable.columns; 243 244 var cur = editor.getCursor(); 245 var result = []; 246 var token = editor.getTokenAt(cur), start, end, search; 247 if (token.end > cur.ch) { 248 token.end = cur.ch; 249 token.string = token.string.slice(0, cur.ch - token.start); 250 } 251 252 if (token.string.match(/^[.`\w@]\w*$/)) { 253 search = token.string; 254 start = token.start; 255 end = token.end; 256 } else { 257 start = end = cur.ch; 258 search = ""; 259 } 260 if (search.charAt(0) == "." || search.charAt(0) == "`") { 261 start = nameCompletion(cur, token, result, editor); 262 } else { 263 addMatches(result, search, tables, function(w) {return w;}); 264 addMatches(result, search, defaultTable, function(w) {return w;}); 265 if (!disableKeywords) 266 addMatches(result, search, keywords, function(w) {return w.toUpperCase();}); 267 } 268 269 return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)}; 270 }); 271 }); -
src/wp-includes/js/codemirror/addon/hint/xml-hint.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var Pos = CodeMirror.Pos; 15 16 function getHints(cm, options) { 17 var tags = options && options.schemaInfo; 18 var quote = (options && options.quoteChar) || '"'; 19 if (!tags) return; 20 var cur = cm.getCursor(), token = cm.getTokenAt(cur); 21 if (token.end > cur.ch) { 22 token.end = cur.ch; 23 token.string = token.string.slice(0, cur.ch - token.start); 24 } 25 var inner = CodeMirror.innerMode(cm.getMode(), token.state); 26 if (inner.mode.name != "xml") return; 27 var result = [], replaceToken = false, prefix; 28 var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string); 29 var tagName = tag && /^\w/.test(token.string), tagStart; 30 31 if (tagName) { 32 var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start); 33 var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null; 34 if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1); 35 } else if (tag && token.string == "<") { 36 tagType = "open"; 37 } else if (tag && token.string == "</") { 38 tagType = "close"; 39 } 40 41 if (!tag && !inner.state.tagName || tagType) { 42 if (tagName) 43 prefix = token.string; 44 replaceToken = tagType; 45 var cx = inner.state.context, curTag = cx && tags[cx.tagName]; 46 var childList = cx ? curTag && curTag.children : tags["!top"]; 47 if (childList && tagType != "close") { 48 for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0) 49 result.push("<" + childList[i]); 50 } else if (tagType != "close") { 51 for (var name in tags) 52 if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || name.lastIndexOf(prefix, 0) == 0)) 53 result.push("<" + name); 54 } 55 if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0)) 56 result.push("</" + cx.tagName + ">"); 57 } else { 58 // Attribute completion 59 var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs; 60 var globalAttrs = tags["!attrs"]; 61 if (!attrs && !globalAttrs) return; 62 if (!attrs) { 63 attrs = globalAttrs; 64 } else if (globalAttrs) { // Combine tag-local and global attributes 65 var set = {}; 66 for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm]; 67 for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm]; 68 attrs = set; 69 } 70 if (token.type == "string" || token.string == "=") { // A value 71 var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)), 72 Pos(cur.line, token.type == "string" ? token.start : token.end)); 73 var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues; 74 if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return; 75 if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget 76 if (token.type == "string") { 77 prefix = token.string; 78 var n = 0; 79 if (/['"]/.test(token.string.charAt(0))) { 80 quote = token.string.charAt(0); 81 prefix = token.string.slice(1); 82 n++; 83 } 84 var len = token.string.length; 85 if (/['"]/.test(token.string.charAt(len - 1))) { 86 quote = token.string.charAt(len - 1); 87 prefix = token.string.substr(n, len - 2); 88 } 89 replaceToken = true; 90 } 91 for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0) 92 result.push(quote + atValues[i] + quote); 93 } else { // An attribute name 94 if (token.type == "attribute") { 95 prefix = token.string; 96 replaceToken = true; 97 } 98 for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0)) 99 result.push(attr); 100 } 101 } 102 return { 103 list: result, 104 from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur, 105 to: replaceToken ? Pos(cur.line, token.end) : cur 106 }; 107 } 108 109 CodeMirror.registerHelper("hint", "xml", getHints); 110 }); -
src/wp-includes/js/codemirror/addon/selection/active-line.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 var WRAP_CLASS = "CodeMirror-activeline"; 14 var BACK_CLASS = "CodeMirror-activeline-background"; 15 var GUTT_CLASS = "CodeMirror-activeline-gutter"; 16 17 CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 18 var prev = old == CodeMirror.Init ? false : old; 19 if (val == prev) return 20 if (prev) { 21 cm.off("beforeSelectionChange", selectionChange); 22 clearActiveLines(cm); 23 delete cm.state.activeLines; 24 } 25 if (val) { 26 cm.state.activeLines = []; 27 updateActiveLines(cm, cm.listSelections()); 28 cm.on("beforeSelectionChange", selectionChange); 29 } 30 }); 31 32 function clearActiveLines(cm) { 33 for (var i = 0; i < cm.state.activeLines.length; i++) { 34 cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 35 cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 36 cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); 37 } 38 } 39 40 function sameArray(a, b) { 41 if (a.length != b.length) return false; 42 for (var i = 0; i < a.length; i++) 43 if (a[i] != b[i]) return false; 44 return true; 45 } 46 47 function updateActiveLines(cm, ranges) { 48 var active = []; 49 for (var i = 0; i < ranges.length; i++) { 50 var range = ranges[i]; 51 var option = cm.getOption("styleActiveLine"); 52 if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) 53 continue 54 var line = cm.getLineHandleVisualStart(range.head.line); 55 if (active[active.length - 1] != line) active.push(line); 56 } 57 if (sameArray(cm.state.activeLines, active)) return; 58 cm.operation(function() { 59 clearActiveLines(cm); 60 for (var i = 0; i < active.length; i++) { 61 cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 cm.addLineClass(active[i], "background", BACK_CLASS); 63 cm.addLineClass(active[i], "gutter", GUTT_CLASS); 64 } 65 cm.state.activeLines = active; 66 }); 67 } 68 69 function selectionChange(cm, sel) { 70 updateActiveLines(cm, sel.ranges); 71 } 72 }); -
src/wp-includes/js/codemirror/addon/selection/mark-selection.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 // Because sometimes you need to mark the selected *text*. 5 // 6 // Adds an option 'styleSelectedText' which, when enabled, gives 7 // selected text the CSS class given as option value, or 8 // "CodeMirror-selectedtext" when the value is not a string. 9 10 (function(mod) { 11 if (typeof exports == "object" && typeof module == "object") // CommonJS 12 mod(require("../../lib/codemirror")); 13 else if (typeof define == "function" && define.amd) // AMD 14 define(["../../lib/codemirror"], mod); 15 else // Plain browser env 16 mod(CodeMirror); 17 })(function(CodeMirror) { 18 "use strict"; 19 20 CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { 21 var prev = old && old != CodeMirror.Init; 22 if (val && !prev) { 23 cm.state.markedSelection = []; 24 cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; 25 reset(cm); 26 cm.on("cursorActivity", onCursorActivity); 27 cm.on("change", onChange); 28 } else if (!val && prev) { 29 cm.off("cursorActivity", onCursorActivity); 30 cm.off("change", onChange); 31 clear(cm); 32 cm.state.markedSelection = cm.state.markedSelectionStyle = null; 33 } 34 }); 35 36 function onCursorActivity(cm) { 37 cm.operation(function() { update(cm); }); 38 } 39 40 function onChange(cm) { 41 if (cm.state.markedSelection.length) 42 cm.operation(function() { clear(cm); }); 43 } 44 45 var CHUNK_SIZE = 8; 46 var Pos = CodeMirror.Pos; 47 var cmp = CodeMirror.cmpPos; 48 49 function coverRange(cm, from, to, addAt) { 50 if (cmp(from, to) == 0) return; 51 var array = cm.state.markedSelection; 52 var cls = cm.state.markedSelectionStyle; 53 for (var line = from.line;;) { 54 var start = line == from.line ? from : Pos(line, 0); 55 var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; 56 var end = atEnd ? to : Pos(endLine, 0); 57 var mark = cm.markText(start, end, {className: cls}); 58 if (addAt == null) array.push(mark); 59 else array.splice(addAt++, 0, mark); 60 if (atEnd) break; 61 line = endLine; 62 } 63 } 64 65 function clear(cm) { 66 var array = cm.state.markedSelection; 67 for (var i = 0; i < array.length; ++i) array[i].clear(); 68 array.length = 0; 69 } 70 71 function reset(cm) { 72 clear(cm); 73 var ranges = cm.listSelections(); 74 for (var i = 0; i < ranges.length; i++) 75 coverRange(cm, ranges[i].from(), ranges[i].to()); 76 } 77 78 function update(cm) { 79 if (!cm.somethingSelected()) return clear(cm); 80 if (cm.listSelections().length > 1) return reset(cm); 81 82 var from = cm.getCursor("start"), to = cm.getCursor("end"); 83 84 var array = cm.state.markedSelection; 85 if (!array.length) return coverRange(cm, from, to); 86 87 var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); 88 if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || 89 cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) 90 return reset(cm); 91 92 while (cmp(from, coverStart.from) > 0) { 93 array.shift().clear(); 94 coverStart = array[0].find(); 95 } 96 if (cmp(from, coverStart.from) < 0) { 97 if (coverStart.to.line - from.line < CHUNK_SIZE) { 98 array.shift().clear(); 99 coverRange(cm, from, coverStart.to, 0); 100 } else { 101 coverRange(cm, from, coverStart.from, 0); 102 } 103 } 104 105 while (cmp(to, coverEnd.to) < 0) { 106 array.pop().clear(); 107 coverEnd = array[array.length - 1].find(); 108 } 109 if (cmp(to, coverEnd.to) > 0) { 110 if (to.line - coverEnd.from.line < CHUNK_SIZE) { 111 array.pop().clear(); 112 coverRange(cm, coverEnd.from, to); 113 } else { 114 coverRange(cm, coverEnd.to, to); 115 } 116 } 117 } 118 }); -
src/wp-includes/js/codemirror/addon/selection/selection-pointer.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineOption("selectionPointer", false, function(cm, val) { 15 var data = cm.state.selectionPointer; 16 if (data) { 17 CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove); 18 CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout); 19 CodeMirror.off(window, "scroll", data.windowScroll); 20 cm.off("cursorActivity", reset); 21 cm.off("scroll", reset); 22 cm.state.selectionPointer = null; 23 cm.display.lineDiv.style.cursor = ""; 24 } 25 if (val) { 26 data = cm.state.selectionPointer = { 27 value: typeof val == "string" ? val : "default", 28 mousemove: function(event) { mousemove(cm, event); }, 29 mouseout: function(event) { mouseout(cm, event); }, 30 windowScroll: function() { reset(cm); }, 31 rects: null, 32 mouseX: null, mouseY: null, 33 willUpdate: false 34 }; 35 CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove); 36 CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout); 37 CodeMirror.on(window, "scroll", data.windowScroll); 38 cm.on("cursorActivity", reset); 39 cm.on("scroll", reset); 40 } 41 }); 42 43 function mousemove(cm, event) { 44 var data = cm.state.selectionPointer; 45 if (event.buttons == null ? event.which : event.buttons) { 46 data.mouseX = data.mouseY = null; 47 } else { 48 data.mouseX = event.clientX; 49 data.mouseY = event.clientY; 50 } 51 scheduleUpdate(cm); 52 } 53 54 function mouseout(cm, event) { 55 if (!cm.getWrapperElement().contains(event.relatedTarget)) { 56 var data = cm.state.selectionPointer; 57 data.mouseX = data.mouseY = null; 58 scheduleUpdate(cm); 59 } 60 } 61 62 function reset(cm) { 63 cm.state.selectionPointer.rects = null; 64 scheduleUpdate(cm); 65 } 66 67 function scheduleUpdate(cm) { 68 if (!cm.state.selectionPointer.willUpdate) { 69 cm.state.selectionPointer.willUpdate = true; 70 setTimeout(function() { 71 update(cm); 72 cm.state.selectionPointer.willUpdate = false; 73 }, 50); 74 } 75 } 76 77 function update(cm) { 78 var data = cm.state.selectionPointer; 79 if (!data) return; 80 if (data.rects == null && data.mouseX != null) { 81 data.rects = []; 82 if (cm.somethingSelected()) { 83 for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling) 84 data.rects.push(sel.getBoundingClientRect()); 85 } 86 } 87 var inside = false; 88 if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) { 89 var rect = data.rects[i]; 90 if (rect.left <= data.mouseX && rect.right >= data.mouseX && 91 rect.top <= data.mouseY && rect.bottom >= data.mouseY) 92 inside = true; 93 } 94 var cursor = inside ? data.value : ""; 95 if (cm.display.lineDiv.style.cursor != cursor) 96 cm.display.lineDiv.style.cursor = cursor; 97 } 98 }); -
src/wp-includes/js/codemirror/lib/codemirror.css
1 /* BASICS */ 2 3 .CodeMirror { 4 /* Set height, width, borders, and global font properties here */ 5 font-family: monospace; 6 height: 300px; 7 color: black; 8 } 9 10 /* PADDING */ 11 12 .CodeMirror-lines { 13 padding: 4px 0; /* Vertical padding around content */ 14 } 15 .CodeMirror pre { 16 padding: 0 4px; /* Horizontal padding of content */ 17 } 18 19 .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 background-color: white; /* The little square between H and V scrollbars */ 21 } 22 23 /* GUTTER */ 24 25 .CodeMirror-gutters { 26 border-right: 1px solid #ddd; 27 background-color: #f7f7f7; 28 white-space: nowrap; 29 } 30 .CodeMirror-linenumbers {} 31 .CodeMirror-linenumber { 32 padding: 0 3px 0 5px; 33 min-width: 20px; 34 text-align: right; 35 color: #999; 36 white-space: nowrap; 37 } 38 39 .CodeMirror-guttermarker { color: black; } 40 .CodeMirror-guttermarker-subtle { color: #999; } 41 42 /* CURSOR */ 43 44 .CodeMirror-cursor { 45 border-left: 1px solid black; 46 border-right: none; 47 width: 0; 48 } 49 /* Shown when moving in bi-directional text */ 50 .CodeMirror div.CodeMirror-secondarycursor { 51 border-left: 1px solid silver; 52 } 53 .cm-fat-cursor .CodeMirror-cursor { 54 width: auto; 55 border: 0 !important; 56 background: #7e7; 57 } 58 .cm-fat-cursor div.CodeMirror-cursors { 59 z-index: 1; 60 } 61 62 .cm-animate-fat-cursor { 63 width: auto; 64 border: 0; 65 -webkit-animation: blink 1.06s steps(1) infinite; 66 -moz-animation: blink 1.06s steps(1) infinite; 67 animation: blink 1.06s steps(1) infinite; 68 background-color: #7e7; 69 } 70 @-moz-keyframes blink { 71 0% {} 72 50% { background-color: transparent; } 73 100% {} 74 } 75 @-webkit-keyframes blink { 76 0% {} 77 50% { background-color: transparent; } 78 100% {} 79 } 80 @keyframes blink { 81 0% {} 82 50% { background-color: transparent; } 83 100% {} 84 } 85 86 /* Can style cursor different in overwrite (non-insert) mode */ 87 .CodeMirror-overwrite .CodeMirror-cursor {} 88 89 .cm-tab { display: inline-block; text-decoration: inherit; } 90 91 .CodeMirror-rulers { 92 position: absolute; 93 left: 0; right: 0; top: -50px; bottom: -20px; 94 overflow: hidden; 95 } 96 .CodeMirror-ruler { 97 border-left: 1px solid #ccc; 98 top: 0; bottom: 0; 99 position: absolute; 100 } 101 102 /* DEFAULT THEME */ 103 104 .cm-s-default .cm-header {color: blue;} 105 .cm-s-default .cm-quote {color: #090;} 106 .cm-negative {color: #d44;} 107 .cm-positive {color: #292;} 108 .cm-header, .cm-strong {font-weight: bold;} 109 .cm-em {font-style: italic;} 110 .cm-link {text-decoration: underline;} 111 .cm-strikethrough {text-decoration: line-through;} 112 113 .cm-s-default .cm-keyword {color: #708;} 114 .cm-s-default .cm-atom {color: #219;} 115 .cm-s-default .cm-number {color: #164;} 116 .cm-s-default .cm-def {color: #00f;} 117 .cm-s-default .cm-variable, 118 .cm-s-default .cm-punctuation, 119 .cm-s-default .cm-property, 120 .cm-s-default .cm-operator {} 121 .cm-s-default .cm-variable-2 {color: #05a;} 122 .cm-s-default .cm-variable-3 {color: #085;} 123 .cm-s-default .cm-comment {color: #a50;} 124 .cm-s-default .cm-string {color: #a11;} 125 .cm-s-default .cm-string-2 {color: #f50;} 126 .cm-s-default .cm-meta {color: #555;} 127 .cm-s-default .cm-qualifier {color: #555;} 128 .cm-s-default .cm-builtin {color: #30a;} 129 .cm-s-default .cm-bracket {color: #997;} 130 .cm-s-default .cm-tag {color: #170;} 131 .cm-s-default .cm-attribute {color: #00c;} 132 .cm-s-default .cm-hr {color: #999;} 133 .cm-s-default .cm-link {color: #00c;} 134 135 .cm-s-default .cm-error {color: #f00;} 136 .cm-invalidchar {color: #f00;} 137 138 .CodeMirror-composing { border-bottom: 2px solid; } 139 140 /* Default styles for common addons */ 141 142 div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 143 div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 144 .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 145 .CodeMirror-activeline-background {background: #e8f2ff;} 146 147 /* STOP */ 148 149 /* The rest of this file contains styles related to the mechanics of 150 the editor. You probably shouldn't touch them. */ 151 152 .CodeMirror { 153 position: relative; 154 overflow: hidden; 155 background: white; 156 } 157 158 .CodeMirror-scroll { 159 overflow: scroll !important; /* Things will break if this is overridden */ 160 /* 30px is the magic margin used to hide the element's real scrollbars */ 161 /* See overflow: hidden in .CodeMirror */ 162 margin-bottom: -30px; margin-right: -30px; 163 padding-bottom: 30px; 164 height: 100%; 165 outline: none; /* Prevent dragging from highlighting the element */ 166 position: relative; 167 } 168 .CodeMirror-sizer { 169 position: relative; 170 border-right: 30px solid transparent; 171 } 172 173 /* The fake, visible scrollbars. Used to force redraw during scrolling 174 before actual scrolling happens, thus preventing shaking and 175 flickering artifacts. */ 176 .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 177 position: absolute; 178 z-index: 6; 179 display: none; 180 } 181 .CodeMirror-vscrollbar { 182 right: 0; top: 0; 183 overflow-x: hidden; 184 overflow-y: scroll; 185 } 186 .CodeMirror-hscrollbar { 187 bottom: 0; left: 0; 188 overflow-y: hidden; 189 overflow-x: scroll; 190 } 191 .CodeMirror-scrollbar-filler { 192 right: 0; bottom: 0; 193 } 194 .CodeMirror-gutter-filler { 195 left: 0; bottom: 0; 196 } 197 198 .CodeMirror-gutters { 199 position: absolute; left: 0; top: 0; 200 min-height: 100%; 201 z-index: 3; 202 } 203 .CodeMirror-gutter { 204 white-space: normal; 205 height: 100%; 206 display: inline-block; 207 vertical-align: top; 208 margin-bottom: -30px; 209 } 210 .CodeMirror-gutter-wrapper { 211 position: absolute; 212 z-index: 4; 213 background: none !important; 214 border: none !important; 215 } 216 .CodeMirror-gutter-background { 217 position: absolute; 218 top: 0; bottom: 0; 219 z-index: 4; 220 } 221 .CodeMirror-gutter-elt { 222 position: absolute; 223 cursor: default; 224 z-index: 4; 225 } 226 .CodeMirror-gutter-wrapper { 227 -webkit-user-select: none; 228 -moz-user-select: none; 229 user-select: none; 230 } 231 232 .CodeMirror-lines { 233 cursor: text; 234 min-height: 1px; /* prevents collapsing before first draw */ 235 } 236 .CodeMirror pre { 237 /* Reset some styles that the rest of the page might have set */ 238 -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 239 border-width: 0; 240 background: transparent; 241 font-family: inherit; 242 font-size: inherit; 243 margin: 0; 244 white-space: pre; 245 word-wrap: normal; 246 line-height: inherit; 247 color: inherit; 248 z-index: 2; 249 position: relative; 250 overflow: visible; 251 -webkit-tap-highlight-color: transparent; 252 -webkit-font-variant-ligatures: contextual; 253 font-variant-ligatures: contextual; 254 } 255 .CodeMirror-wrap pre { 256 word-wrap: break-word; 257 white-space: pre-wrap; 258 word-break: normal; 259 } 260 261 .CodeMirror-linebackground { 262 position: absolute; 263 left: 0; right: 0; top: 0; bottom: 0; 264 z-index: 0; 265 } 266 267 .CodeMirror-linewidget { 268 position: relative; 269 z-index: 2; 270 overflow: auto; 271 } 272 273 .CodeMirror-widget {} 274 275 .CodeMirror-code { 276 outline: none; 277 } 278 279 /* Force content-box sizing for the elements where we expect it */ 280 .CodeMirror-scroll, 281 .CodeMirror-sizer, 282 .CodeMirror-gutter, 283 .CodeMirror-gutters, 284 .CodeMirror-linenumber { 285 -moz-box-sizing: content-box; 286 box-sizing: content-box; 287 } 288 289 .CodeMirror-measure { 290 position: absolute; 291 width: 100%; 292 height: 0; 293 overflow: hidden; 294 visibility: hidden; 295 } 296 297 .CodeMirror-cursor { 298 position: absolute; 299 pointer-events: none; 300 } 301 .CodeMirror-measure pre { position: static; } 302 303 div.CodeMirror-cursors { 304 visibility: hidden; 305 position: relative; 306 z-index: 3; 307 } 308 div.CodeMirror-dragcursors { 309 visibility: visible; 310 } 311 312 .CodeMirror-focused div.CodeMirror-cursors { 313 visibility: visible; 314 } 315 316 .CodeMirror-selected { background: #d9d9d9; } 317 .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 318 .CodeMirror-crosshair { cursor: crosshair; } 319 .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 320 .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 321 322 .cm-searching { 323 background: #ffa; 324 background: rgba(255, 255, 0, .4); 325 } 326 327 /* Used to force a border model for a node */ 328 .cm-force-border { padding-right: .1px; } 329 330 @media print { 331 /* Hide the cursor when printing */ 332 .CodeMirror div.CodeMirror-cursors { 333 visibility: hidden; 334 } 335 } 336 337 /* See issue #2901 */ 338 .cm-tab-wrap-hack:after { content: ''; } 339 340 /* Help users use markselection to safely style text background */ 341 span.CodeMirror-selectedtext { background: none; } -
src/wp-includes/js/codemirror/lib/codemirror.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 // This is CodeMirror (http://codemirror.net), a code editor 5 // implemented in JavaScript on top of the browser's DOM. 6 // 7 // You can find some technical background for some of the code below 8 // at http://marijnhaverbeke.nl/blog/#cm-internals . 9 10 (function (global, factory) { 11 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 12 typeof define === 'function' && define.amd ? define(factory) : 13 (global.CodeMirror = factory()); 14 }(this, (function () { 'use strict'; 15 16 // Kludges for bugs and behavior differences that can't be feature 17 // detected are enabled based on userAgent etc sniffing. 18 var userAgent = navigator.userAgent 19 var platform = navigator.platform 20 21 var gecko = /gecko\/\d/i.test(userAgent) 22 var ie_upto10 = /MSIE \d/.test(userAgent) 23 var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) 24 var ie = ie_upto10 || ie_11up 25 var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]) 26 var webkit = /WebKit\//.test(userAgent) 27 var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) 28 var chrome = /Chrome\//.test(userAgent) 29 var presto = /Opera\//.test(userAgent) 30 var safari = /Apple Computer/.test(navigator.vendor) 31 var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) 32 var phantom = /PhantomJS/.test(userAgent) 33 34 var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) 35 // This is woefully incomplete. Suggestions for alternative methods welcome. 36 var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) 37 var mac = ios || /Mac/.test(platform) 38 var chromeOS = /\bCrOS\b/.test(userAgent) 39 var windows = /win/i.test(platform) 40 41 var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/) 42 if (presto_version) { presto_version = Number(presto_version[1]) } 43 if (presto_version && presto_version >= 15) { presto = false; webkit = true } 44 // Some browsers use the wrong event properties to signal cmd/ctrl on OS X 45 var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)) 46 var captureRightClick = gecko || (ie && ie_version >= 9) 47 48 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } 49 50 var rmClass = function(node, cls) { 51 var current = node.className 52 var match = classTest(cls).exec(current) 53 if (match) { 54 var after = current.slice(match.index + match[0].length) 55 node.className = current.slice(0, match.index) + (after ? match[1] + after : "") 56 } 57 } 58 59 function removeChildren(e) { 60 for (var count = e.childNodes.length; count > 0; --count) 61 { e.removeChild(e.firstChild) } 62 return e 63 } 64 65 function removeChildrenAndAdd(parent, e) { 66 return removeChildren(parent).appendChild(e) 67 } 68 69 function elt(tag, content, className, style) { 70 var e = document.createElement(tag) 71 if (className) { e.className = className } 72 if (style) { e.style.cssText = style } 73 if (typeof content == "string") { e.appendChild(document.createTextNode(content)) } 74 else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]) } } 75 return e 76 } 77 78 var range 79 if (document.createRange) { range = function(node, start, end, endNode) { 80 var r = document.createRange() 81 r.setEnd(endNode || node, end) 82 r.setStart(node, start) 83 return r 84 } } 85 else { range = function(node, start, end) { 86 var r = document.body.createTextRange() 87 try { r.moveToElementText(node.parentNode) } 88 catch(e) { return r } 89 r.collapse(true) 90 r.moveEnd("character", end) 91 r.moveStart("character", start) 92 return r 93 } } 94 95 function contains(parent, child) { 96 if (child.nodeType == 3) // Android browser always returns false when child is a textnode 97 { child = child.parentNode } 98 if (parent.contains) 99 { return parent.contains(child) } 100 do { 101 if (child.nodeType == 11) { child = child.host } 102 if (child == parent) { return true } 103 } while (child = child.parentNode) 104 } 105 106 function activeElt() { 107 // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. 108 // IE < 10 will throw when accessed while the page is loading or in an iframe. 109 // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. 110 var activeElement 111 try { 112 activeElement = document.activeElement 113 } catch(e) { 114 activeElement = document.body || null 115 } 116 while (activeElement && activeElement.root && activeElement.root.activeElement) 117 { activeElement = activeElement.root.activeElement } 118 return activeElement 119 } 120 121 function addClass(node, cls) { 122 var current = node.className 123 if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls } 124 } 125 function joinClasses(a, b) { 126 var as = a.split(" ") 127 for (var i = 0; i < as.length; i++) 128 { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i] } } 129 return b 130 } 131 132 var selectInput = function(node) { node.select() } 133 if (ios) // Mobile Safari apparently has a bug where select() is broken. 134 { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } } 135 else if (ie) // Suppress mysterious IE10 errors 136 { selectInput = function(node) { try { node.select() } catch(_e) {} } } 137 138 function bind(f) { 139 var args = Array.prototype.slice.call(arguments, 1) 140 return function(){return f.apply(null, args)} 141 } 142 143 function copyObj(obj, target, overwrite) { 144 if (!target) { target = {} } 145 for (var prop in obj) 146 { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) 147 { target[prop] = obj[prop] } } 148 return target 149 } 150 151 // Counts the column offset in a string, taking tabs into account. 152 // Used mostly to find indentation. 153 function countColumn(string, end, tabSize, startIndex, startValue) { 154 if (end == null) { 155 end = string.search(/[^\s\u00a0]/) 156 if (end == -1) { end = string.length } 157 } 158 for (var i = startIndex || 0, n = startValue || 0;;) { 159 var nextTab = string.indexOf("\t", i) 160 if (nextTab < 0 || nextTab >= end) 161 { return n + (end - i) } 162 n += nextTab - i 163 n += tabSize - (n % tabSize) 164 i = nextTab + 1 165 } 166 } 167 168 function Delayed() {this.id = null} 169 Delayed.prototype.set = function(ms, f) { 170 clearTimeout(this.id) 171 this.id = setTimeout(f, ms) 172 } 173 174 function indexOf(array, elt) { 175 for (var i = 0; i < array.length; ++i) 176 { if (array[i] == elt) { return i } } 177 return -1 178 } 179 180 // Number of pixels added to scroller and sizer to hide scrollbar 181 var scrollerGap = 30 182 183 // Returned or thrown by various protocols to signal 'I'm not 184 // handling this'. 185 var Pass = {toString: function(){return "CodeMirror.Pass"}} 186 187 // Reused option objects for setSelection & friends 188 var sel_dontScroll = {scroll: false}; 189 var sel_mouse = {origin: "*mouse"}; 190 var sel_move = {origin: "+move"}; 191 // The inverse of countColumn -- find the offset that corresponds to 192 // a particular column. 193 function findColumn(string, goal, tabSize) { 194 for (var pos = 0, col = 0;;) { 195 var nextTab = string.indexOf("\t", pos) 196 if (nextTab == -1) { nextTab = string.length } 197 var skipped = nextTab - pos 198 if (nextTab == string.length || col + skipped >= goal) 199 { return pos + Math.min(skipped, goal - col) } 200 col += nextTab - pos 201 col += tabSize - (col % tabSize) 202 pos = nextTab + 1 203 if (col >= goal) { return pos } 204 } 205 } 206 207 var spaceStrs = [""] 208 function spaceStr(n) { 209 while (spaceStrs.length <= n) 210 { spaceStrs.push(lst(spaceStrs) + " ") } 211 return spaceStrs[n] 212 } 213 214 function lst(arr) { return arr[arr.length-1] } 215 216 function map(array, f) { 217 var out = [] 218 for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i) } 219 return out 220 } 221 222 function insertSorted(array, value, score) { 223 var pos = 0, priority = score(value) 224 while (pos < array.length && score(array[pos]) <= priority) { pos++ } 225 array.splice(pos, 0, value) 226 } 227 228 function nothing() {} 229 230 function createObj(base, props) { 231 var inst 232 if (Object.create) { 233 inst = Object.create(base) 234 } else { 235 nothing.prototype = base 236 inst = new nothing() 237 } 238 if (props) { copyObj(props, inst) } 239 return inst 240 } 241 242 var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/ 243 function isWordCharBasic(ch) { 244 return /\w/.test(ch) || ch > "\x80" && 245 (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) 246 } 247 function isWordChar(ch, helper) { 248 if (!helper) { return isWordCharBasic(ch) } 249 if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } 250 return helper.test(ch) 251 } 252 253 function isEmpty(obj) { 254 for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } 255 return true 256 } 257 258 // Extending unicode characters. A series of a non-extending char + 259 // any number of extending chars is treated as a single unit as far 260 // as editing and measuring is concerned. This is not fully correct, 261 // since some scripts/fonts/browsers also treat other configurations 262 // of code points as a group. 263 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]/ 264 function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } 265 266 // The display handles the DOM integration, both for input reading 267 // and content drawing. It holds references to DOM nodes and 268 // display-related state. 269 270 function Display(place, doc, input) { 271 var d = this 272 this.input = input 273 274 // Covers bottom-right square when both scrollbars are present. 275 d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler") 276 d.scrollbarFiller.setAttribute("cm-not-content", "true") 277 // Covers bottom of gutter when coverGutterNextToScrollbar is on 278 // and h scrollbar is present. 279 d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler") 280 d.gutterFiller.setAttribute("cm-not-content", "true") 281 // Will contain the actual code, positioned to cover the viewport. 282 d.lineDiv = elt("div", null, "CodeMirror-code") 283 // Elements are added to these to represent selection and cursors. 284 d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1") 285 d.cursorDiv = elt("div", null, "CodeMirror-cursors") 286 // A visibility: hidden element used to find the size of things. 287 d.measure = elt("div", null, "CodeMirror-measure") 288 // When lines outside of the viewport are measured, they are drawn in this. 289 d.lineMeasure = elt("div", null, "CodeMirror-measure") 290 // Wraps everything that needs to exist inside the vertically-padded coordinate system 291 d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], 292 null, "position: relative; outline: none") 293 // Moved around its parent to cover visible view. 294 d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative") 295 // Set to the height of the document, allowing scrolling. 296 d.sizer = elt("div", [d.mover], "CodeMirror-sizer") 297 d.sizerWidth = null 298 // Behavior of elts with overflow: auto and padding is 299 // inconsistent across browsers. This is used to ensure the 300 // scrollable area is big enough. 301 d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;") 302 // Will contain the gutters, if any. 303 d.gutters = elt("div", null, "CodeMirror-gutters") 304 d.lineGutter = null 305 // Actual scrollable element. 306 d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll") 307 d.scroller.setAttribute("tabIndex", "-1") 308 // The element in which the editor lives. 309 d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror") 310 311 // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) 312 if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 } 313 if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true } 314 315 if (place) { 316 if (place.appendChild) { place.appendChild(d.wrapper) } 317 else { place(d.wrapper) } 318 } 319 320 // Current rendered range (may be bigger than the view window). 321 d.viewFrom = d.viewTo = doc.first 322 d.reportedViewFrom = d.reportedViewTo = doc.first 323 // Information about the rendered lines. 324 d.view = [] 325 d.renderedView = null 326 // Holds info about a single rendered line when it was rendered 327 // for measurement, while not in view. 328 d.externalMeasured = null 329 // Empty space (in pixels) above the view 330 d.viewOffset = 0 331 d.lastWrapHeight = d.lastWrapWidth = 0 332 d.updateLineNumbers = null 333 334 d.nativeBarWidth = d.barHeight = d.barWidth = 0 335 d.scrollbarsClipped = false 336 337 // Used to only resize the line number gutter when necessary (when 338 // the amount of lines crosses a boundary that makes its width change) 339 d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null 340 // Set to true when a non-horizontal-scrolling line widget is 341 // added. As an optimization, line widget aligning is skipped when 342 // this is false. 343 d.alignWidgets = false 344 345 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null 346 347 // Tracks the maximum line length so that the horizontal scrollbar 348 // can be kept static when scrolling. 349 d.maxLine = null 350 d.maxLineLength = 0 351 d.maxLineChanged = false 352 353 // Used for measuring wheel scrolling granularity 354 d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null 355 356 // True when shift is held down. 357 d.shift = false 358 359 // Used to track whether anything happened since the context menu 360 // was opened. 361 d.selForContextMenu = null 362 363 d.activeTouch = null 364 365 input.init(d) 366 } 367 368 // Find the line object corresponding to the given line number. 369 function getLine(doc, n) { 370 n -= doc.first 371 if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } 372 var chunk = doc 373 while (!chunk.lines) { 374 for (var i = 0;; ++i) { 375 var child = chunk.children[i], sz = child.chunkSize() 376 if (n < sz) { chunk = child; break } 377 n -= sz 378 } 379 } 380 return chunk.lines[n] 381 } 382 383 // Get the part of a document between two positions, as an array of 384 // strings. 385 function getBetween(doc, start, end) { 386 var out = [], n = start.line 387 doc.iter(start.line, end.line + 1, function (line) { 388 var text = line.text 389 if (n == end.line) { text = text.slice(0, end.ch) } 390 if (n == start.line) { text = text.slice(start.ch) } 391 out.push(text) 392 ++n 393 }) 394 return out 395 } 396 // Get the lines between from and to, as array of strings. 397 function getLines(doc, from, to) { 398 var out = [] 399 doc.iter(from, to, function (line) { out.push(line.text) }) // iter aborts when callback returns truthy value 400 return out 401 } 402 403 // Update the height of a line, propagating the height change 404 // upwards to parent nodes. 405 function updateLineHeight(line, height) { 406 var diff = height - line.height 407 if (diff) { for (var n = line; n; n = n.parent) { n.height += diff } } 408 } 409 410 // Given a line object, find its line number by walking up through 411 // its parent links. 412 function lineNo(line) { 413 if (line.parent == null) { return null } 414 var cur = line.parent, no = indexOf(cur.lines, line) 415 for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { 416 for (var i = 0;; ++i) { 417 if (chunk.children[i] == cur) { break } 418 no += chunk.children[i].chunkSize() 419 } 420 } 421 return no + cur.first 422 } 423 424 // Find the line at the given vertical position, using the height 425 // information in the document tree. 426 function lineAtHeight(chunk, h) { 427 var n = chunk.first 428 outer: do { 429 for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { 430 var child = chunk.children[i$1], ch = child.height 431 if (h < ch) { chunk = child; continue outer } 432 h -= ch 433 n += child.chunkSize() 434 } 435 return n 436 } while (!chunk.lines) 437 var i = 0 438 for (; i < chunk.lines.length; ++i) { 439 var line = chunk.lines[i], lh = line.height 440 if (h < lh) { break } 441 h -= lh 442 } 443 return n + i 444 } 445 446 function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} 447 448 function lineNumberFor(options, i) { 449 return String(options.lineNumberFormatter(i + options.firstLineNumber)) 450 } 451 452 // A Pos instance represents a position within the text. 453 function Pos (line, ch) { 454 if (!(this instanceof Pos)) { return new Pos(line, ch) } 455 this.line = line; this.ch = ch 456 } 457 458 // Compare two positions, return 0 if they are the same, a negative 459 // number when a is less, and a positive number otherwise. 460 function cmp(a, b) { return a.line - b.line || a.ch - b.ch } 461 462 function copyPos(x) {return Pos(x.line, x.ch)} 463 function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } 464 function minPos(a, b) { return cmp(a, b) < 0 ? a : b } 465 466 // Most of the external API clips given positions to make sure they 467 // actually exist within the document. 468 function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} 469 function clipPos(doc, pos) { 470 if (pos.line < doc.first) { return Pos(doc.first, 0) } 471 var last = doc.first + doc.size - 1 472 if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } 473 return clipToLen(pos, getLine(doc, pos.line).text.length) 474 } 475 function clipToLen(pos, linelen) { 476 var ch = pos.ch 477 if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } 478 else if (ch < 0) { return Pos(pos.line, 0) } 479 else { return pos } 480 } 481 function clipPosArray(doc, array) { 482 var out = [] 483 for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]) } 484 return out 485 } 486 487 // Optimize some code when these features are not used. 488 var sawReadOnlySpans = false; 489 var sawCollapsedSpans = false; 490 function seeReadOnlySpans() { 491 sawReadOnlySpans = true 492 } 493 494 function seeCollapsedSpans() { 495 sawCollapsedSpans = true 496 } 497 498 // TEXTMARKER SPANS 499 500 function MarkedSpan(marker, from, to) { 501 this.marker = marker 502 this.from = from; this.to = to 503 } 504 505 // Search an array of spans for a span matching the given marker. 506 function getMarkedSpanFor(spans, marker) { 507 if (spans) { for (var i = 0; i < spans.length; ++i) { 508 var span = spans[i] 509 if (span.marker == marker) { return span } 510 } } 511 } 512 // Remove a span from an array, returning undefined if no spans are 513 // left (we don't store arrays for lines without spans). 514 function removeMarkedSpan(spans, span) { 515 var r 516 for (var i = 0; i < spans.length; ++i) 517 { if (spans[i] != span) { (r || (r = [])).push(spans[i]) } } 518 return r 519 } 520 // Add a span to a line. 521 function addMarkedSpan(line, span) { 522 line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span] 523 span.marker.attachLine(line) 524 } 525 526 // Used for the algorithm that adjusts markers for a change in the 527 // document. These functions cut an array of spans at a given 528 // character position, returning an array of remaining chunks (or 529 // undefined if nothing remains). 530 function markedSpansBefore(old, startCh, isInsert) { 531 var nw 532 if (old) { for (var i = 0; i < old.length; ++i) { 533 var span = old[i], marker = span.marker 534 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh) 535 if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { 536 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) 537 ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)) 538 } 539 } } 540 return nw 541 } 542 function markedSpansAfter(old, endCh, isInsert) { 543 var nw 544 if (old) { for (var i = 0; i < old.length; ++i) { 545 var span = old[i], marker = span.marker 546 var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh) 547 if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { 548 var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) 549 ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, 550 span.to == null ? null : span.to - endCh)) 551 } 552 } } 553 return nw 554 } 555 556 // Given a change object, compute the new set of marker spans that 557 // cover the line in which the change took place. Removes spans 558 // entirely within the change, reconnects spans belonging to the 559 // same marker that appear on both sides of the change, and cuts off 560 // spans partially within the change. Returns an array of span 561 // arrays with one element for each line in (after) the change. 562 function stretchSpansOverChange(doc, change) { 563 if (change.full) { return null } 564 var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans 565 var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans 566 if (!oldFirst && !oldLast) { return null } 567 568 var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0 569 // Get the spans that 'stick out' on both sides 570 var first = markedSpansBefore(oldFirst, startCh, isInsert) 571 var last = markedSpansAfter(oldLast, endCh, isInsert) 572 573 // Next, merge those two ends 574 var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0) 575 if (first) { 576 // Fix up .to properties of first 577 for (var i = 0; i < first.length; ++i) { 578 var span = first[i] 579 if (span.to == null) { 580 var found = getMarkedSpanFor(last, span.marker) 581 if (!found) { span.to = startCh } 582 else if (sameLine) { span.to = found.to == null ? null : found.to + offset } 583 } 584 } 585 } 586 if (last) { 587 // Fix up .from in last (or move them into first in case of sameLine) 588 for (var i$1 = 0; i$1 < last.length; ++i$1) { 589 var span$1 = last[i$1] 590 if (span$1.to != null) { span$1.to += offset } 591 if (span$1.from == null) { 592 var found$1 = getMarkedSpanFor(first, span$1.marker) 593 if (!found$1) { 594 span$1.from = offset 595 if (sameLine) { (first || (first = [])).push(span$1) } 596 } 597 } else { 598 span$1.from += offset 599 if (sameLine) { (first || (first = [])).push(span$1) } 600 } 601 } 602 } 603 // Make sure we didn't create any zero-length spans 604 if (first) { first = clearEmptySpans(first) } 605 if (last && last != first) { last = clearEmptySpans(last) } 606 607 var newMarkers = [first] 608 if (!sameLine) { 609 // Fill gap with whole-line-spans 610 var gap = change.text.length - 2, gapMarkers 611 if (gap > 0 && first) 612 { for (var i$2 = 0; i$2 < first.length; ++i$2) 613 { if (first[i$2].to == null) 614 { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)) } } } 615 for (var i$3 = 0; i$3 < gap; ++i$3) 616 { newMarkers.push(gapMarkers) } 617 newMarkers.push(last) 618 } 619 return newMarkers 620 } 621 622 // Remove spans that are empty and don't have a clearWhenEmpty 623 // option of false. 624 function clearEmptySpans(spans) { 625 for (var i = 0; i < spans.length; ++i) { 626 var span = spans[i] 627 if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) 628 { spans.splice(i--, 1) } 629 } 630 if (!spans.length) { return null } 631 return spans 632 } 633 634 // Used to 'clip' out readOnly ranges when making a change. 635 function removeReadOnlyRanges(doc, from, to) { 636 var markers = null 637 doc.iter(from.line, to.line + 1, function (line) { 638 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { 639 var mark = line.markedSpans[i].marker 640 if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) 641 { (markers || (markers = [])).push(mark) } 642 } } 643 }) 644 if (!markers) { return null } 645 var parts = [{from: from, to: to}] 646 for (var i = 0; i < markers.length; ++i) { 647 var mk = markers[i], m = mk.find(0) 648 for (var j = 0; j < parts.length; ++j) { 649 var p = parts[j] 650 if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } 651 var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to) 652 if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) 653 { newParts.push({from: p.from, to: m.from}) } 654 if (dto > 0 || !mk.inclusiveRight && !dto) 655 { newParts.push({from: m.to, to: p.to}) } 656 parts.splice.apply(parts, newParts) 657 j += newParts.length - 1 658 } 659 } 660 return parts 661 } 662 663 // Connect or disconnect spans from a line. 664 function detachMarkedSpans(line) { 665 var spans = line.markedSpans 666 if (!spans) { return } 667 for (var i = 0; i < spans.length; ++i) 668 { spans[i].marker.detachLine(line) } 669 line.markedSpans = null 670 } 671 function attachMarkedSpans(line, spans) { 672 if (!spans) { return } 673 for (var i = 0; i < spans.length; ++i) 674 { spans[i].marker.attachLine(line) } 675 line.markedSpans = spans 676 } 677 678 // Helpers used when computing which overlapping collapsed span 679 // counts as the larger one. 680 function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } 681 function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } 682 683 // Returns a number indicating which of two overlapping collapsed 684 // spans is larger (and thus includes the other). Falls back to 685 // comparing ids when the spans cover exactly the same range. 686 function compareCollapsedMarkers(a, b) { 687 var lenDiff = a.lines.length - b.lines.length 688 if (lenDiff != 0) { return lenDiff } 689 var aPos = a.find(), bPos = b.find() 690 var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b) 691 if (fromCmp) { return -fromCmp } 692 var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b) 693 if (toCmp) { return toCmp } 694 return b.id - a.id 695 } 696 697 // Find out whether a line ends or starts in a collapsed span. If 698 // so, return the marker for that span. 699 function collapsedSpanAtSide(line, start) { 700 var sps = sawCollapsedSpans && line.markedSpans, found 701 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { 702 sp = sps[i] 703 if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && 704 (!found || compareCollapsedMarkers(found, sp.marker) < 0)) 705 { found = sp.marker } 706 } } 707 return found 708 } 709 function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } 710 function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } 711 712 // Test whether there exists a collapsed span that partially 713 // overlaps (covers the start or end, but not both) of a new span. 714 // Such overlap is not allowed. 715 function conflictingCollapsedRange(doc, lineNo, from, to, marker) { 716 var line = getLine(doc, lineNo) 717 var sps = sawCollapsedSpans && line.markedSpans 718 if (sps) { for (var i = 0; i < sps.length; ++i) { 719 var sp = sps[i] 720 if (!sp.marker.collapsed) { continue } 721 var found = sp.marker.find(0) 722 var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker) 723 var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker) 724 if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } 725 if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || 726 fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) 727 { return true } 728 } } 729 } 730 731 // A visual line is a line as drawn on the screen. Folding, for 732 // example, can cause multiple logical lines to appear on the same 733 // visual line. This finds the start of the visual line that the 734 // given line is part of (usually that is the line itself). 735 function visualLine(line) { 736 var merged 737 while (merged = collapsedSpanAtStart(line)) 738 { line = merged.find(-1, true).line } 739 return line 740 } 741 742 // Returns an array of logical lines that continue the visual line 743 // started by the argument, or undefined if there are no such lines. 744 function visualLineContinued(line) { 745 var merged, lines 746 while (merged = collapsedSpanAtEnd(line)) { 747 line = merged.find(1, true).line 748 ;(lines || (lines = [])).push(line) 749 } 750 return lines 751 } 752 753 // Get the line number of the start of the visual line that the 754 // given line number is part of. 755 function visualLineNo(doc, lineN) { 756 var line = getLine(doc, lineN), vis = visualLine(line) 757 if (line == vis) { return lineN } 758 return lineNo(vis) 759 } 760 761 // Get the line number of the start of the next visual line after 762 // the given line. 763 function visualLineEndNo(doc, lineN) { 764 if (lineN > doc.lastLine()) { return lineN } 765 var line = getLine(doc, lineN), merged 766 if (!lineIsHidden(doc, line)) { return lineN } 767 while (merged = collapsedSpanAtEnd(line)) 768 { line = merged.find(1, true).line } 769 return lineNo(line) + 1 770 } 771 772 // Compute whether a line is hidden. Lines count as hidden when they 773 // are part of a visual line that starts with another line, or when 774 // they are entirely covered by collapsed, non-widget span. 775 function lineIsHidden(doc, line) { 776 var sps = sawCollapsedSpans && line.markedSpans 777 if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { 778 sp = sps[i] 779 if (!sp.marker.collapsed) { continue } 780 if (sp.from == null) { return true } 781 if (sp.marker.widgetNode) { continue } 782 if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) 783 { return true } 784 } } 785 } 786 function lineIsHiddenInner(doc, line, span) { 787 if (span.to == null) { 788 var end = span.marker.find(1, true) 789 return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) 790 } 791 if (span.marker.inclusiveRight && span.to == line.text.length) 792 { return true } 793 for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { 794 sp = line.markedSpans[i] 795 if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && 796 (sp.to == null || sp.to != span.from) && 797 (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && 798 lineIsHiddenInner(doc, line, sp)) { return true } 799 } 800 } 801 802 // Find the height above the given line. 803 function heightAtLine(lineObj) { 804 lineObj = visualLine(lineObj) 805 806 var h = 0, chunk = lineObj.parent 807 for (var i = 0; i < chunk.lines.length; ++i) { 808 var line = chunk.lines[i] 809 if (line == lineObj) { break } 810 else { h += line.height } 811 } 812 for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { 813 for (var i$1 = 0; i$1 < p.children.length; ++i$1) { 814 var cur = p.children[i$1] 815 if (cur == chunk) { break } 816 else { h += cur.height } 817 } 818 } 819 return h 820 } 821 822 // Compute the character length of a line, taking into account 823 // collapsed ranges (see markText) that might hide parts, and join 824 // other lines onto it. 825 function lineLength(line) { 826 if (line.height == 0) { return 0 } 827 var len = line.text.length, merged, cur = line 828 while (merged = collapsedSpanAtStart(cur)) { 829 var found = merged.find(0, true) 830 cur = found.from.line 831 len += found.from.ch - found.to.ch 832 } 833 cur = line 834 while (merged = collapsedSpanAtEnd(cur)) { 835 var found$1 = merged.find(0, true) 836 len -= cur.text.length - found$1.from.ch 837 cur = found$1.to.line 838 len += cur.text.length - found$1.to.ch 839 } 840 return len 841 } 842 843 // Find the longest line in the document. 844 function findMaxLine(cm) { 845 var d = cm.display, doc = cm.doc 846 d.maxLine = getLine(doc, doc.first) 847 d.maxLineLength = lineLength(d.maxLine) 848 d.maxLineChanged = true 849 doc.iter(function (line) { 850 var len = lineLength(line) 851 if (len > d.maxLineLength) { 852 d.maxLineLength = len 853 d.maxLine = line 854 } 855 }) 856 } 857 858 // BIDI HELPERS 859 860 function iterateBidiSections(order, from, to, f) { 861 if (!order) { return f(from, to, "ltr") } 862 var found = false 863 for (var i = 0; i < order.length; ++i) { 864 var part = order[i] 865 if (part.from < to && part.to > from || from == to && part.to == from) { 866 f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr") 867 found = true 868 } 869 } 870 if (!found) { f(from, to, "ltr") } 871 } 872 873 function bidiLeft(part) { return part.level % 2 ? part.to : part.from } 874 function bidiRight(part) { return part.level % 2 ? part.from : part.to } 875 876 function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 } 877 function lineRight(line) { 878 var order = getOrder(line) 879 if (!order) { return line.text.length } 880 return bidiRight(lst(order)) 881 } 882 883 function compareBidiLevel(order, a, b) { 884 var linedir = order[0].level 885 if (a == linedir) { return true } 886 if (b == linedir) { return false } 887 return a < b 888 } 889 890 var bidiOther = null 891 function getBidiPartAt(order, pos) { 892 var found 893 bidiOther = null 894 for (var i = 0; i < order.length; ++i) { 895 var cur = order[i] 896 if (cur.from < pos && cur.to > pos) { return i } 897 if ((cur.from == pos || cur.to == pos)) { 898 if (found == null) { 899 found = i 900 } else if (compareBidiLevel(order, cur.level, order[found].level)) { 901 if (cur.from != cur.to) { bidiOther = found } 902 return i 903 } else { 904 if (cur.from != cur.to) { bidiOther = i } 905 return found 906 } 907 } 908 } 909 return found 910 } 911 912 function moveInLine(line, pos, dir, byUnit) { 913 if (!byUnit) { return pos + dir } 914 do { pos += dir } 915 while (pos > 0 && isExtendingChar(line.text.charAt(pos))) 916 return pos 917 } 918 919 // This is needed in order to move 'visually' through bi-directional 920 // text -- i.e., pressing left should make the cursor go left, even 921 // when in RTL text. The tricky part is the 'jumps', where RTL and 922 // LTR text touch each other. This often requires the cursor offset 923 // to move more than one unit, in order to visually move one unit. 924 function moveVisually(line, start, dir, byUnit) { 925 var bidi = getOrder(line) 926 if (!bidi) { return moveLogically(line, start, dir, byUnit) } 927 var pos = getBidiPartAt(bidi, start), part = bidi[pos] 928 var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit) 929 930 for (;;) { 931 if (target > part.from && target < part.to) { return target } 932 if (target == part.from || target == part.to) { 933 if (getBidiPartAt(bidi, target) == pos) { return target } 934 part = bidi[pos += dir] 935 return (dir > 0) == part.level % 2 ? part.to : part.from 936 } else { 937 part = bidi[pos += dir] 938 if (!part) { return null } 939 if ((dir > 0) == part.level % 2) 940 { target = moveInLine(line, part.to, -1, byUnit) } 941 else 942 { target = moveInLine(line, part.from, 1, byUnit) } 943 } 944 } 945 } 946 947 function moveLogically(line, start, dir, byUnit) { 948 var target = start + dir 949 if (byUnit) { while (target > 0 && isExtendingChar(line.text.charAt(target))) { target += dir } } 950 return target < 0 || target > line.text.length ? null : target 951 } 952 953 // Bidirectional ordering algorithm 954 // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm 955 // that this (partially) implements. 956 957 // One-char codes used for character types: 958 // L (L): Left-to-Right 959 // R (R): Right-to-Left 960 // r (AL): Right-to-Left Arabic 961 // 1 (EN): European Number 962 // + (ES): European Number Separator 963 // % (ET): European Number Terminator 964 // n (AN): Arabic Number 965 // , (CS): Common Number Separator 966 // m (NSM): Non-Spacing Mark 967 // b (BN): Boundary Neutral 968 // s (B): Paragraph Separator 969 // t (S): Segment Separator 970 // w (WS): Whitespace 971 // N (ON): Other Neutrals 972 973 // Returns null if characters are ordered as they appear 974 // (left-to-right), or an array of sections ({from, to, level} 975 // objects) in the order in which they occur visually. 976 var bidiOrdering = (function() { 977 // Character types for codepoints 0 to 0xff 978 var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" 979 // Character types for codepoints 0x600 to 0x6f9 980 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111" 981 function charType(code) { 982 if (code <= 0xf7) { return lowTypes.charAt(code) } 983 else if (0x590 <= code && code <= 0x5f4) { return "R" } 984 else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } 985 else if (0x6ee <= code && code <= 0x8ac) { return "r" } 986 else if (0x2000 <= code && code <= 0x200b) { return "w" } 987 else if (code == 0x200c) { return "b" } 988 else { return "L" } 989 } 990 991 var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/ 992 var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/ 993 // Browsers seem to always treat the boundaries of block elements as being L. 994 var outerType = "L" 995 996 function BidiSpan(level, from, to) { 997 this.level = level 998 this.from = from; this.to = to 999 } 1000 1001 return function(str) { 1002 if (!bidiRE.test(str)) { return false } 1003 var len = str.length, types = [] 1004 for (var i = 0; i < len; ++i) 1005 { types.push(charType(str.charCodeAt(i))) } 1006 1007 // W1. Examine each non-spacing mark (NSM) in the level run, and 1008 // change the type of the NSM to the type of the previous 1009 // character. If the NSM is at the start of the level run, it will 1010 // get the type of sor. 1011 for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { 1012 var type = types[i$1] 1013 if (type == "m") { types[i$1] = prev } 1014 else { prev = type } 1015 } 1016 1017 // W2. Search backwards from each instance of a European number 1018 // until the first strong type (R, L, AL, or sor) is found. If an 1019 // AL is found, change the type of the European number to Arabic 1020 // number. 1021 // W3. Change all ALs to R. 1022 for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { 1023 var type$1 = types[i$2] 1024 if (type$1 == "1" && cur == "r") { types[i$2] = "n" } 1025 else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R" } } 1026 } 1027 1028 // W4. A single European separator between two European numbers 1029 // changes to a European number. A single common separator between 1030 // two numbers of the same type changes to that type. 1031 for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { 1032 var type$2 = types[i$3] 1033 if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1" } 1034 else if (type$2 == "," && prev$1 == types[i$3+1] && 1035 (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1 } 1036 prev$1 = type$2 1037 } 1038 1039 // W5. A sequence of European terminators adjacent to European 1040 // numbers changes to all European numbers. 1041 // W6. Otherwise, separators and terminators change to Other 1042 // Neutral. 1043 for (var i$4 = 0; i$4 < len; ++i$4) { 1044 var type$3 = types[i$4] 1045 if (type$3 == ",") { types[i$4] = "N" } 1046 else if (type$3 == "%") { 1047 var end = (void 0) 1048 for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} 1049 var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N" 1050 for (var j = i$4; j < end; ++j) { types[j] = replace } 1051 i$4 = end - 1 1052 } 1053 } 1054 1055 // W7. Search backwards from each instance of a European number 1056 // until the first strong type (R, L, or sor) is found. If an L is 1057 // found, then change the type of the European number to L. 1058 for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { 1059 var type$4 = types[i$5] 1060 if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L" } 1061 else if (isStrong.test(type$4)) { cur$1 = type$4 } 1062 } 1063 1064 // N1. A sequence of neutrals takes the direction of the 1065 // surrounding strong text if the text on both sides has the same 1066 // direction. European and Arabic numbers act as if they were R in 1067 // terms of their influence on neutrals. Start-of-level-run (sor) 1068 // and end-of-level-run (eor) are used at level run boundaries. 1069 // N2. Any remaining neutrals take the embedding direction. 1070 for (var i$6 = 0; i$6 < len; ++i$6) { 1071 if (isNeutral.test(types[i$6])) { 1072 var end$1 = (void 0) 1073 for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} 1074 var before = (i$6 ? types[i$6-1] : outerType) == "L" 1075 var after = (end$1 < len ? types[end$1] : outerType) == "L" 1076 var replace$1 = before || after ? "L" : "R" 1077 for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1 } 1078 i$6 = end$1 - 1 1079 } 1080 } 1081 1082 // Here we depart from the documented algorithm, in order to avoid 1083 // building up an actual levels array. Since there are only three 1084 // levels (0, 1, 2) in an implementation that doesn't take 1085 // explicit embedding into account, we can build up the order on 1086 // the fly, without following the level-based algorithm. 1087 var order = [], m 1088 for (var i$7 = 0; i$7 < len;) { 1089 if (countsAsLeft.test(types[i$7])) { 1090 var start = i$7 1091 for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} 1092 order.push(new BidiSpan(0, start, i$7)) 1093 } else { 1094 var pos = i$7, at = order.length 1095 for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} 1096 for (var j$2 = pos; j$2 < i$7;) { 1097 if (countsAsNum.test(types[j$2])) { 1098 if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)) } 1099 var nstart = j$2 1100 for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} 1101 order.splice(at, 0, new BidiSpan(2, nstart, j$2)) 1102 pos = j$2 1103 } else { ++j$2 } 1104 } 1105 if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)) } 1106 } 1107 } 1108 if (order[0].level == 1 && (m = str.match(/^\s+/))) { 1109 order[0].from = m[0].length 1110 order.unshift(new BidiSpan(0, 0, m[0].length)) 1111 } 1112 if (lst(order).level == 1 && (m = str.match(/\s+$/))) { 1113 lst(order).to -= m[0].length 1114 order.push(new BidiSpan(0, len - m[0].length, len)) 1115 } 1116 if (order[0].level == 2) 1117 { order.unshift(new BidiSpan(1, order[0].to, order[0].to)) } 1118 if (order[0].level != lst(order).level) 1119 { order.push(new BidiSpan(order[0].level, len, len)) } 1120 1121 return order 1122 } 1123 })() 1124 1125 // Get the bidi ordering for the given line (and cache it). Returns 1126 // false for lines that are fully left-to-right, and an array of 1127 // BidiSpan objects otherwise. 1128 function getOrder(line) { 1129 var order = line.order 1130 if (order == null) { order = line.order = bidiOrdering(line.text) } 1131 return order 1132 } 1133 1134 // EVENT HANDLING 1135 1136 // Lightweight event framework. on/off also work on DOM nodes, 1137 // registering native DOM handlers. 1138 1139 var noHandlers = [] 1140 1141 var on = function(emitter, type, f) { 1142 if (emitter.addEventListener) { 1143 emitter.addEventListener(type, f, false) 1144 } else if (emitter.attachEvent) { 1145 emitter.attachEvent("on" + type, f) 1146 } else { 1147 var map = emitter._handlers || (emitter._handlers = {}) 1148 map[type] = (map[type] || noHandlers).concat(f) 1149 } 1150 } 1151 1152 function getHandlers(emitter, type) { 1153 return emitter._handlers && emitter._handlers[type] || noHandlers 1154 } 1155 1156 function off(emitter, type, f) { 1157 if (emitter.removeEventListener) { 1158 emitter.removeEventListener(type, f, false) 1159 } else if (emitter.detachEvent) { 1160 emitter.detachEvent("on" + type, f) 1161 } else { 1162 var map = emitter._handlers, arr = map && map[type] 1163 if (arr) { 1164 var index = indexOf(arr, f) 1165 if (index > -1) 1166 { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) } 1167 } 1168 } 1169 } 1170 1171 function signal(emitter, type /*, values...*/) { 1172 var handlers = getHandlers(emitter, type) 1173 if (!handlers.length) { return } 1174 var args = Array.prototype.slice.call(arguments, 2) 1175 for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args) } 1176 } 1177 1178 // The DOM events that CodeMirror handles can be overridden by 1179 // registering a (non-DOM) handler on the editor for the event name, 1180 // and preventDefault-ing the event in that handler. 1181 function signalDOMEvent(cm, e, override) { 1182 if (typeof e == "string") 1183 { e = {type: e, preventDefault: function() { this.defaultPrevented = true }} } 1184 signal(cm, override || e.type, cm, e) 1185 return e_defaultPrevented(e) || e.codemirrorIgnore 1186 } 1187 1188 function signalCursorActivity(cm) { 1189 var arr = cm._handlers && cm._handlers.cursorActivity 1190 if (!arr) { return } 1191 var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []) 1192 for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) 1193 { set.push(arr[i]) } } 1194 } 1195 1196 function hasHandler(emitter, type) { 1197 return getHandlers(emitter, type).length > 0 1198 } 1199 1200 // Add on and off methods to a constructor's prototype, to make 1201 // registering events on such objects more convenient. 1202 function eventMixin(ctor) { 1203 ctor.prototype.on = function(type, f) {on(this, type, f)} 1204 ctor.prototype.off = function(type, f) {off(this, type, f)} 1205 } 1206 1207 // Due to the fact that we still support jurassic IE versions, some 1208 // compatibility wrappers are needed. 1209 1210 function e_preventDefault(e) { 1211 if (e.preventDefault) { e.preventDefault() } 1212 else { e.returnValue = false } 1213 } 1214 function e_stopPropagation(e) { 1215 if (e.stopPropagation) { e.stopPropagation() } 1216 else { e.cancelBubble = true } 1217 } 1218 function e_defaultPrevented(e) { 1219 return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false 1220 } 1221 function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)} 1222 1223 function e_target(e) {return e.target || e.srcElement} 1224 function e_button(e) { 1225 var b = e.which 1226 if (b == null) { 1227 if (e.button & 1) { b = 1 } 1228 else if (e.button & 2) { b = 3 } 1229 else if (e.button & 4) { b = 2 } 1230 } 1231 if (mac && e.ctrlKey && b == 1) { b = 3 } 1232 return b 1233 } 1234 1235 // Detect drag-and-drop 1236 var dragAndDrop = function() { 1237 // There is *some* kind of drag-and-drop support in IE6-8, but I 1238 // couldn't get it to work yet. 1239 if (ie && ie_version < 9) { return false } 1240 var div = elt('div') 1241 return "draggable" in div || "dragDrop" in div 1242 }() 1243 1244 var zwspSupported 1245 function zeroWidthElement(measure) { 1246 if (zwspSupported == null) { 1247 var test = elt("span", "\u200b") 1248 removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])) 1249 if (measure.firstChild.offsetHeight != 0) 1250 { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) } 1251 } 1252 var node = zwspSupported ? elt("span", "\u200b") : 1253 elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px") 1254 node.setAttribute("cm-text", "") 1255 return node 1256 } 1257 1258 // Feature-detect IE's crummy client rect reporting for bidi text 1259 var badBidiRects 1260 function hasBadBidiRects(measure) { 1261 if (badBidiRects != null) { return badBidiRects } 1262 var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) 1263 var r0 = range(txt, 0, 1).getBoundingClientRect() 1264 var r1 = range(txt, 1, 2).getBoundingClientRect() 1265 removeChildren(measure) 1266 if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) 1267 return badBidiRects = (r1.right - r0.right < 3) 1268 } 1269 1270 // See if "".split is the broken IE version, if so, provide an 1271 // alternative way to split lines. 1272 var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { 1273 var pos = 0, result = [], l = string.length 1274 while (pos <= l) { 1275 var nl = string.indexOf("\n", pos) 1276 if (nl == -1) { nl = string.length } 1277 var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl) 1278 var rt = line.indexOf("\r") 1279 if (rt != -1) { 1280 result.push(line.slice(0, rt)) 1281 pos += rt + 1 1282 } else { 1283 result.push(line) 1284 pos = nl + 1 1285 } 1286 } 1287 return result 1288 } : function (string) { return string.split(/\r\n?|\n/); } 1289 1290 var hasSelection = window.getSelection ? function (te) { 1291 try { return te.selectionStart != te.selectionEnd } 1292 catch(e) { return false } 1293 } : function (te) { 1294 var range 1295 try {range = te.ownerDocument.selection.createRange()} 1296 catch(e) {} 1297 if (!range || range.parentElement() != te) { return false } 1298 return range.compareEndPoints("StartToEnd", range) != 0 1299 } 1300 1301 var hasCopyEvent = (function () { 1302 var e = elt("div") 1303 if ("oncopy" in e) { return true } 1304 e.setAttribute("oncopy", "return;") 1305 return typeof e.oncopy == "function" 1306 })() 1307 1308 var badZoomedRects = null 1309 function hasBadZoomedRects(measure) { 1310 if (badZoomedRects != null) { return badZoomedRects } 1311 var node = removeChildrenAndAdd(measure, elt("span", "x")) 1312 var normal = node.getBoundingClientRect() 1313 var fromRange = range(node, 0, 1).getBoundingClientRect() 1314 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 1315 } 1316 1317 var modes = {}; 1318 var mimeModes = {}; 1319 // Extra arguments are stored as the mode's dependencies, which is 1320 // used by (legacy) mechanisms like loadmode.js to automatically 1321 // load a mode. (Preferred mechanism is the require/define calls.) 1322 function defineMode(name, mode) { 1323 if (arguments.length > 2) 1324 { mode.dependencies = Array.prototype.slice.call(arguments, 2) } 1325 modes[name] = mode 1326 } 1327 1328 function defineMIME(mime, spec) { 1329 mimeModes[mime] = spec 1330 } 1331 1332 // Given a MIME type, a {name, ...options} config object, or a name 1333 // string, return a mode config object. 1334 function resolveMode(spec) { 1335 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { 1336 spec = mimeModes[spec] 1337 } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { 1338 var found = mimeModes[spec.name] 1339 if (typeof found == "string") { found = {name: found} } 1340 spec = createObj(found, spec) 1341 spec.name = found.name 1342 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { 1343 return resolveMode("application/xml") 1344 } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { 1345 return resolveMode("application/json") 1346 } 1347 if (typeof spec == "string") { return {name: spec} } 1348 else { return spec || {name: "null"} } 1349 } 1350 1351 // Given a mode spec (anything that resolveMode accepts), find and 1352 // initialize an actual mode object. 1353 function getMode(options, spec) { 1354 spec = resolveMode(spec) 1355 var mfactory = modes[spec.name] 1356 if (!mfactory) { return getMode(options, "text/plain") } 1357 var modeObj = mfactory(options, spec) 1358 if (modeExtensions.hasOwnProperty(spec.name)) { 1359 var exts = modeExtensions[spec.name] 1360 for (var prop in exts) { 1361 if (!exts.hasOwnProperty(prop)) { continue } 1362 if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop] } 1363 modeObj[prop] = exts[prop] 1364 } 1365 } 1366 modeObj.name = spec.name 1367 if (spec.helperType) { modeObj.helperType = spec.helperType } 1368 if (spec.modeProps) { for (var prop$1 in spec.modeProps) 1369 { modeObj[prop$1] = spec.modeProps[prop$1] } } 1370 1371 return modeObj 1372 } 1373 1374 // This can be used to attach properties to mode objects from 1375 // outside the actual mode definition. 1376 var modeExtensions = {} 1377 function extendMode(mode, properties) { 1378 var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}) 1379 copyObj(properties, exts) 1380 } 1381 1382 function copyState(mode, state) { 1383 if (state === true) { return state } 1384 if (mode.copyState) { return mode.copyState(state) } 1385 var nstate = {} 1386 for (var n in state) { 1387 var val = state[n] 1388 if (val instanceof Array) { val = val.concat([]) } 1389 nstate[n] = val 1390 } 1391 return nstate 1392 } 1393 1394 // Given a mode and a state (for that mode), find the inner mode and 1395 // state at the position that the state refers to. 1396 function innerMode(mode, state) { 1397 var info 1398 while (mode.innerMode) { 1399 info = mode.innerMode(state) 1400 if (!info || info.mode == mode) { break } 1401 state = info.state 1402 mode = info.mode 1403 } 1404 return info || {mode: mode, state: state} 1405 } 1406 1407 function startState(mode, a1, a2) { 1408 return mode.startState ? mode.startState(a1, a2) : true 1409 } 1410 1411 // STRING STREAM 1412 1413 // Fed to the mode parsers, provides helper functions to make 1414 // parsers more succinct. 1415 1416 var StringStream = function(string, tabSize) { 1417 this.pos = this.start = 0 1418 this.string = string 1419 this.tabSize = tabSize || 8 1420 this.lastColumnPos = this.lastColumnValue = 0 1421 this.lineStart = 0 1422 } 1423 1424 StringStream.prototype = { 1425 eol: function() {return this.pos >= this.string.length}, 1426 sol: function() {return this.pos == this.lineStart}, 1427 peek: function() {return this.string.charAt(this.pos) || undefined}, 1428 next: function() { 1429 if (this.pos < this.string.length) 1430 { return this.string.charAt(this.pos++) } 1431 }, 1432 eat: function(match) { 1433 var ch = this.string.charAt(this.pos) 1434 var ok 1435 if (typeof match == "string") { ok = ch == match } 1436 else { ok = ch && (match.test ? match.test(ch) : match(ch)) } 1437 if (ok) {++this.pos; return ch} 1438 }, 1439 eatWhile: function(match) { 1440 var start = this.pos 1441 while (this.eat(match)){} 1442 return this.pos > start 1443 }, 1444 eatSpace: function() { 1445 var this$1 = this; 1446 1447 var start = this.pos 1448 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos } 1449 return this.pos > start 1450 }, 1451 skipToEnd: function() {this.pos = this.string.length}, 1452 skipTo: function(ch) { 1453 var found = this.string.indexOf(ch, this.pos) 1454 if (found > -1) {this.pos = found; return true} 1455 }, 1456 backUp: function(n) {this.pos -= n}, 1457 column: function() { 1458 if (this.lastColumnPos < this.start) { 1459 this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue) 1460 this.lastColumnPos = this.start 1461 } 1462 return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) 1463 }, 1464 indentation: function() { 1465 return countColumn(this.string, null, this.tabSize) - 1466 (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) 1467 }, 1468 match: function(pattern, consume, caseInsensitive) { 1469 if (typeof pattern == "string") { 1470 var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; } 1471 var substr = this.string.substr(this.pos, pattern.length) 1472 if (cased(substr) == cased(pattern)) { 1473 if (consume !== false) { this.pos += pattern.length } 1474 return true 1475 } 1476 } else { 1477 var match = this.string.slice(this.pos).match(pattern) 1478 if (match && match.index > 0) { return null } 1479 if (match && consume !== false) { this.pos += match[0].length } 1480 return match 1481 } 1482 }, 1483 current: function(){return this.string.slice(this.start, this.pos)}, 1484 hideFirstChars: function(n, inner) { 1485 this.lineStart += n 1486 try { return inner() } 1487 finally { this.lineStart -= n } 1488 } 1489 } 1490 1491 // Compute a style array (an array starting with a mode generation 1492 // -- for invalidation -- followed by pairs of end positions and 1493 // style strings), which is used to highlight the tokens on the 1494 // line. 1495 function highlightLine(cm, line, state, forceToEnd) { 1496 // A styles array always starts with a number identifying the 1497 // mode/overlays that it is based on (for easy invalidation). 1498 var st = [cm.state.modeGen], lineClasses = {} 1499 // Compute the base array of styles 1500 runMode(cm, line.text, cm.doc.mode, state, function (end, style) { return st.push(end, style); }, 1501 lineClasses, forceToEnd) 1502 1503 // Run overlays, adjust style array. 1504 var loop = function ( o ) { 1505 var overlay = cm.state.overlays[o], i = 1, at = 0 1506 runMode(cm, line.text, overlay.mode, true, function (end, style) { 1507 var start = i 1508 // Ensure there's a token end at the current position, and that i points at it 1509 while (at < end) { 1510 var i_end = st[i] 1511 if (i_end > end) 1512 { st.splice(i, 1, end, st[i+1], i_end) } 1513 i += 2 1514 at = Math.min(end, i_end) 1515 } 1516 if (!style) { return } 1517 if (overlay.opaque) { 1518 st.splice(start, i - start, end, "overlay " + style) 1519 i = start + 2 1520 } else { 1521 for (; start < i; start += 2) { 1522 var cur = st[start+1] 1523 st[start+1] = (cur ? cur + " " : "") + "overlay " + style 1524 } 1525 } 1526 }, lineClasses) 1527 }; 1528 1529 for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); 1530 1531 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} 1532 } 1533 1534 function getLineStyles(cm, line, updateFrontier) { 1535 if (!line.styles || line.styles[0] != cm.state.modeGen) { 1536 var state = getStateBefore(cm, lineNo(line)) 1537 var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state) 1538 line.stateAfter = state 1539 line.styles = result.styles 1540 if (result.classes) { line.styleClasses = result.classes } 1541 else if (line.styleClasses) { line.styleClasses = null } 1542 if (updateFrontier === cm.doc.frontier) { cm.doc.frontier++ } 1543 } 1544 return line.styles 1545 } 1546 1547 function getStateBefore(cm, n, precise) { 1548 var doc = cm.doc, display = cm.display 1549 if (!doc.mode.startState) { return true } 1550 var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter 1551 if (!state) { state = startState(doc.mode) } 1552 else { state = copyState(doc.mode, state) } 1553 doc.iter(pos, n, function (line) { 1554 processLine(cm, line.text, state) 1555 var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo 1556 line.stateAfter = save ? copyState(doc.mode, state) : null 1557 ++pos 1558 }) 1559 if (precise) { doc.frontier = pos } 1560 return state 1561 } 1562 1563 // Lightweight form of highlight -- proceed over this line and 1564 // update state, but don't save a style array. Used for lines that 1565 // aren't currently visible. 1566 function processLine(cm, text, state, startAt) { 1567 var mode = cm.doc.mode 1568 var stream = new StringStream(text, cm.options.tabSize) 1569 stream.start = stream.pos = startAt || 0 1570 if (text == "") { callBlankLine(mode, state) } 1571 while (!stream.eol()) { 1572 readToken(mode, stream, state) 1573 stream.start = stream.pos 1574 } 1575 } 1576 1577 function callBlankLine(mode, state) { 1578 if (mode.blankLine) { return mode.blankLine(state) } 1579 if (!mode.innerMode) { return } 1580 var inner = innerMode(mode, state) 1581 if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } 1582 } 1583 1584 function readToken(mode, stream, state, inner) { 1585 for (var i = 0; i < 10; i++) { 1586 if (inner) { inner[0] = innerMode(mode, state).mode } 1587 var style = mode.token(stream, state) 1588 if (stream.pos > stream.start) { return style } 1589 } 1590 throw new Error("Mode " + mode.name + " failed to advance stream.") 1591 } 1592 1593 // Utility for getTokenAt and getLineTokens 1594 function takeToken(cm, pos, precise, asArray) { 1595 var getObj = function (copy) { return ({ 1596 start: stream.start, end: stream.pos, 1597 string: stream.current(), 1598 type: style || null, 1599 state: copy ? copyState(doc.mode, state) : state 1600 }); } 1601 1602 var doc = cm.doc, mode = doc.mode, style 1603 pos = clipPos(doc, pos) 1604 var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise) 1605 var stream = new StringStream(line.text, cm.options.tabSize), tokens 1606 if (asArray) { tokens = [] } 1607 while ((asArray || stream.pos < pos.ch) && !stream.eol()) { 1608 stream.start = stream.pos 1609 style = readToken(mode, stream, state) 1610 if (asArray) { tokens.push(getObj(true)) } 1611 } 1612 return asArray ? tokens : getObj() 1613 } 1614 1615 function extractLineClasses(type, output) { 1616 if (type) { for (;;) { 1617 var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) 1618 if (!lineClass) { break } 1619 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length) 1620 var prop = lineClass[1] ? "bgClass" : "textClass" 1621 if (output[prop] == null) 1622 { output[prop] = lineClass[2] } 1623 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) 1624 { output[prop] += " " + lineClass[2] } 1625 } } 1626 return type 1627 } 1628 1629 // Run the given mode's parser over a line, calling f for each token. 1630 function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { 1631 var flattenSpans = mode.flattenSpans 1632 if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans } 1633 var curStart = 0, curStyle = null 1634 var stream = new StringStream(text, cm.options.tabSize), style 1635 var inner = cm.options.addModeClass && [null] 1636 if (text == "") { extractLineClasses(callBlankLine(mode, state), lineClasses) } 1637 while (!stream.eol()) { 1638 if (stream.pos > cm.options.maxHighlightLength) { 1639 flattenSpans = false 1640 if (forceToEnd) { processLine(cm, text, state, stream.pos) } 1641 stream.pos = text.length 1642 style = null 1643 } else { 1644 style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses) 1645 } 1646 if (inner) { 1647 var mName = inner[0].name 1648 if (mName) { style = "m-" + (style ? mName + " " + style : mName) } 1649 } 1650 if (!flattenSpans || curStyle != style) { 1651 while (curStart < stream.start) { 1652 curStart = Math.min(stream.start, curStart + 5000) 1653 f(curStart, curStyle) 1654 } 1655 curStyle = style 1656 } 1657 stream.start = stream.pos 1658 } 1659 while (curStart < stream.pos) { 1660 // Webkit seems to refuse to render text nodes longer than 57444 1661 // characters, and returns inaccurate measurements in nodes 1662 // starting around 5000 chars. 1663 var pos = Math.min(stream.pos, curStart + 5000) 1664 f(pos, curStyle) 1665 curStart = pos 1666 } 1667 } 1668 1669 // Finds the line to start with when starting a parse. Tries to 1670 // find a line with a stateAfter, so that it can start with a 1671 // valid state. If that fails, it returns the line with the 1672 // smallest indentation, which tends to need the least context to 1673 // parse correctly. 1674 function findStartLine(cm, n, precise) { 1675 var minindent, minline, doc = cm.doc 1676 var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) 1677 for (var search = n; search > lim; --search) { 1678 if (search <= doc.first) { return doc.first } 1679 var line = getLine(doc, search - 1) 1680 if (line.stateAfter && (!precise || search <= doc.frontier)) { return search } 1681 var indented = countColumn(line.text, null, cm.options.tabSize) 1682 if (minline == null || minindent > indented) { 1683 minline = search - 1 1684 minindent = indented 1685 } 1686 } 1687 return minline 1688 } 1689 1690 // LINE DATA STRUCTURE 1691 1692 // Line objects. These hold state related to a line, including 1693 // highlighting info (the styles array). 1694 function Line(text, markedSpans, estimateHeight) { 1695 this.text = text 1696 attachMarkedSpans(this, markedSpans) 1697 this.height = estimateHeight ? estimateHeight(this) : 1 1698 } 1699 eventMixin(Line) 1700 Line.prototype.lineNo = function() { return lineNo(this) } 1701 1702 // Change the content (text, markers) of a line. Automatically 1703 // invalidates cached information and tries to re-estimate the 1704 // line's height. 1705 function updateLine(line, text, markedSpans, estimateHeight) { 1706 line.text = text 1707 if (line.stateAfter) { line.stateAfter = null } 1708 if (line.styles) { line.styles = null } 1709 if (line.order != null) { line.order = null } 1710 detachMarkedSpans(line) 1711 attachMarkedSpans(line, markedSpans) 1712 var estHeight = estimateHeight ? estimateHeight(line) : 1 1713 if (estHeight != line.height) { updateLineHeight(line, estHeight) } 1714 } 1715 1716 // Detach a line from the document tree and its markers. 1717 function cleanUpLine(line) { 1718 line.parent = null 1719 detachMarkedSpans(line) 1720 } 1721 1722 // Convert a style as returned by a mode (either null, or a string 1723 // containing one or more styles) to a CSS style. This is cached, 1724 // and also looks for line-wide styles. 1725 var styleToClassCache = {}; 1726 var styleToClassCacheWithMode = {}; 1727 function interpretTokenStyle(style, options) { 1728 if (!style || /^\s*$/.test(style)) { return null } 1729 var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache 1730 return cache[style] || 1731 (cache[style] = style.replace(/\S+/g, "cm-$&")) 1732 } 1733 1734 // Render the DOM representation of the text of a line. Also builds 1735 // up a 'line map', which points at the DOM nodes that represent 1736 // specific stretches of text, and is used by the measuring code. 1737 // The returned object contains the DOM node, this map, and 1738 // information about line-wide styles that were set by the mode. 1739 function buildLineContent(cm, lineView) { 1740 // The padding-right forces the element to have a 'border', which 1741 // is needed on Webkit to be able to get line-level bounding 1742 // rectangles for it (in measureChar). 1743 var content = elt("span", null, null, webkit ? "padding-right: .1px" : null) 1744 var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content, 1745 col: 0, pos: 0, cm: cm, 1746 trailingSpace: false, 1747 splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")} 1748 // hide from accessibility tree 1749 content.setAttribute("role", "presentation") 1750 builder.pre.setAttribute("role", "presentation") 1751 lineView.measure = {} 1752 1753 // Iterate over the logical lines that make up this visual line. 1754 for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { 1755 var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0) 1756 builder.pos = 0 1757 builder.addToken = buildToken 1758 // Optionally wire in some hacks into the token-rendering 1759 // algorithm, to deal with browser quirks. 1760 if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) 1761 { builder.addToken = buildTokenBadBidi(builder.addToken, order) } 1762 builder.map = [] 1763 var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) 1764 insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)) 1765 if (line.styleClasses) { 1766 if (line.styleClasses.bgClass) 1767 { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") } 1768 if (line.styleClasses.textClass) 1769 { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") } 1770 } 1771 1772 // Ensure at least a single node is present, for measuring. 1773 if (builder.map.length == 0) 1774 { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) } 1775 1776 // Store the map and a cache object for the current logical line 1777 if (i == 0) { 1778 lineView.measure.map = builder.map 1779 lineView.measure.cache = {} 1780 } else { 1781 ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) 1782 ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}) 1783 } 1784 } 1785 1786 // See issue #2901 1787 if (webkit) { 1788 var last = builder.content.lastChild 1789 if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) 1790 { builder.content.className = "cm-tab-wrap-hack" } 1791 } 1792 1793 signal(cm, "renderLine", cm, lineView.line, builder.pre) 1794 if (builder.pre.className) 1795 { builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") } 1796 1797 return builder 1798 } 1799 1800 function defaultSpecialCharPlaceholder(ch) { 1801 var token = elt("span", "\u2022", "cm-invalidchar") 1802 token.title = "\\u" + ch.charCodeAt(0).toString(16) 1803 token.setAttribute("aria-label", token.title) 1804 return token 1805 } 1806 1807 // Build up the DOM representation for a single token, and add it to 1808 // the line map. Takes care to render special characters separately. 1809 function buildToken(builder, text, style, startStyle, endStyle, title, css) { 1810 if (!text) { return } 1811 var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text 1812 var special = builder.cm.state.specialChars, mustWrap = false 1813 var content 1814 if (!special.test(text)) { 1815 builder.col += text.length 1816 content = document.createTextNode(displayText) 1817 builder.map.push(builder.pos, builder.pos + text.length, content) 1818 if (ie && ie_version < 9) { mustWrap = true } 1819 builder.pos += text.length 1820 } else { 1821 content = document.createDocumentFragment() 1822 var pos = 0 1823 while (true) { 1824 special.lastIndex = pos 1825 var m = special.exec(text) 1826 var skipped = m ? m.index - pos : text.length - pos 1827 if (skipped) { 1828 var txt = document.createTextNode(displayText.slice(pos, pos + skipped)) 1829 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])) } 1830 else { content.appendChild(txt) } 1831 builder.map.push(builder.pos, builder.pos + skipped, txt) 1832 builder.col += skipped 1833 builder.pos += skipped 1834 } 1835 if (!m) { break } 1836 pos += skipped + 1 1837 var txt$1 = (void 0) 1838 if (m[0] == "\t") { 1839 var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize 1840 txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) 1841 txt$1.setAttribute("role", "presentation") 1842 txt$1.setAttribute("cm-text", "\t") 1843 builder.col += tabWidth 1844 } else if (m[0] == "\r" || m[0] == "\n") { 1845 txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) 1846 txt$1.setAttribute("cm-text", m[0]) 1847 builder.col += 1 1848 } else { 1849 txt$1 = builder.cm.options.specialCharPlaceholder(m[0]) 1850 txt$1.setAttribute("cm-text", m[0]) 1851 if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])) } 1852 else { content.appendChild(txt$1) } 1853 builder.col += 1 1854 } 1855 builder.map.push(builder.pos, builder.pos + 1, txt$1) 1856 builder.pos++ 1857 } 1858 } 1859 builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32 1860 if (style || startStyle || endStyle || mustWrap || css) { 1861 var fullStyle = style || "" 1862 if (startStyle) { fullStyle += startStyle } 1863 if (endStyle) { fullStyle += endStyle } 1864 var token = elt("span", [content], fullStyle, css) 1865 if (title) { token.title = title } 1866 return builder.content.appendChild(token) 1867 } 1868 builder.content.appendChild(content) 1869 } 1870 1871 function splitSpaces(text, trailingBefore) { 1872 if (text.length > 1 && !/ /.test(text)) { return text } 1873 var spaceBefore = trailingBefore, result = "" 1874 for (var i = 0; i < text.length; i++) { 1875 var ch = text.charAt(i) 1876 if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) 1877 { ch = "\u00a0" } 1878 result += ch 1879 spaceBefore = ch == " " 1880 } 1881 return result 1882 } 1883 1884 // Work around nonsense dimensions being reported for stretches of 1885 // right-to-left text. 1886 function buildTokenBadBidi(inner, order) { 1887 return function (builder, text, style, startStyle, endStyle, title, css) { 1888 style = style ? style + " cm-force-border" : "cm-force-border" 1889 var start = builder.pos, end = start + text.length 1890 for (;;) { 1891 // Find the part that overlaps with the start of this text 1892 var part = (void 0) 1893 for (var i = 0; i < order.length; i++) { 1894 part = order[i] 1895 if (part.to > start && part.from <= start) { break } 1896 } 1897 if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) } 1898 inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css) 1899 startStyle = null 1900 text = text.slice(part.to - start) 1901 start = part.to 1902 } 1903 } 1904 } 1905 1906 function buildCollapsedSpan(builder, size, marker, ignoreWidget) { 1907 var widget = !ignoreWidget && marker.widgetNode 1908 if (widget) { builder.map.push(builder.pos, builder.pos + size, widget) } 1909 if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { 1910 if (!widget) 1911 { widget = builder.content.appendChild(document.createElement("span")) } 1912 widget.setAttribute("cm-marker", marker.id) 1913 } 1914 if (widget) { 1915 builder.cm.display.input.setUneditable(widget) 1916 builder.content.appendChild(widget) 1917 } 1918 builder.pos += size 1919 builder.trailingSpace = false 1920 } 1921 1922 // Outputs a number of spans to make up a line, taking highlighting 1923 // and marked text into account. 1924 function insertLineContent(line, builder, styles) { 1925 var spans = line.markedSpans, allText = line.text, at = 0 1926 if (!spans) { 1927 for (var i$1 = 1; i$1 < styles.length; i$1+=2) 1928 { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)) } 1929 return 1930 } 1931 1932 var len = allText.length, pos = 0, i = 1, text = "", style, css 1933 var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed 1934 for (;;) { 1935 if (nextChange == pos) { // Update current marker set 1936 spanStyle = spanEndStyle = spanStartStyle = title = css = "" 1937 collapsed = null; nextChange = Infinity 1938 var foundBookmarks = [], endStyles = (void 0) 1939 for (var j = 0; j < spans.length; ++j) { 1940 var sp = spans[j], m = sp.marker 1941 if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { 1942 foundBookmarks.push(m) 1943 } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { 1944 if (sp.to != null && sp.to != pos && nextChange > sp.to) { 1945 nextChange = sp.to 1946 spanEndStyle = "" 1947 } 1948 if (m.className) { spanStyle += " " + m.className } 1949 if (m.css) { css = (css ? css + ";" : "") + m.css } 1950 if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle } 1951 if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to) } 1952 if (m.title && !title) { title = m.title } 1953 if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) 1954 { collapsed = sp } 1955 } else if (sp.from > pos && nextChange > sp.from) { 1956 nextChange = sp.from 1957 } 1958 } 1959 if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) 1960 { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1] } } } 1961 1962 if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) 1963 { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]) } } 1964 if (collapsed && (collapsed.from || 0) == pos) { 1965 buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, 1966 collapsed.marker, collapsed.from == null) 1967 if (collapsed.to == null) { return } 1968 if (collapsed.to == pos) { collapsed = false } 1969 } 1970 } 1971 if (pos >= len) { break } 1972 1973 var upto = Math.min(len, nextChange) 1974 while (true) { 1975 if (text) { 1976 var end = pos + text.length 1977 if (!collapsed) { 1978 var tokenText = end > upto ? text.slice(0, upto - pos) : text 1979 builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, 1980 spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css) 1981 } 1982 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} 1983 pos = end 1984 spanStartStyle = "" 1985 } 1986 text = allText.slice(at, at = styles[i++]) 1987 style = interpretTokenStyle(styles[i++], builder.cm.options) 1988 } 1989 } 1990 } 1991 1992 1993 // These objects are used to represent the visible (currently drawn) 1994 // part of the document. A LineView may correspond to multiple 1995 // logical lines, if those are connected by collapsed ranges. 1996 function LineView(doc, line, lineN) { 1997 // The starting line 1998 this.line = line 1999 // Continuing lines, if any 2000 this.rest = visualLineContinued(line) 2001 // Number of logical lines in this visual line 2002 this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1 2003 this.node = this.text = null 2004 this.hidden = lineIsHidden(doc, line) 2005 } 2006 2007 // Create a range of LineView objects for the given lines. 2008 function buildViewArray(cm, from, to) { 2009 var array = [], nextPos 2010 for (var pos = from; pos < to; pos = nextPos) { 2011 var view = new LineView(cm.doc, getLine(cm.doc, pos), pos) 2012 nextPos = pos + view.size 2013 array.push(view) 2014 } 2015 return array 2016 } 2017 2018 var operationGroup = null 2019 2020 function pushOperation(op) { 2021 if (operationGroup) { 2022 operationGroup.ops.push(op) 2023 } else { 2024 op.ownsGroup = operationGroup = { 2025 ops: [op], 2026 delayedCallbacks: [] 2027 } 2028 } 2029 } 2030 2031 function fireCallbacksForOps(group) { 2032 // Calls delayed callbacks and cursorActivity handlers until no 2033 // new ones appear 2034 var callbacks = group.delayedCallbacks, i = 0 2035 do { 2036 for (; i < callbacks.length; i++) 2037 { callbacks[i].call(null) } 2038 for (var j = 0; j < group.ops.length; j++) { 2039 var op = group.ops[j] 2040 if (op.cursorActivityHandlers) 2041 { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) 2042 { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } } 2043 } 2044 } while (i < callbacks.length) 2045 } 2046 2047 function finishOperation(op, endCb) { 2048 var group = op.ownsGroup 2049 if (!group) { return } 2050 2051 try { fireCallbacksForOps(group) } 2052 finally { 2053 operationGroup = null 2054 endCb(group) 2055 } 2056 } 2057 2058 var orphanDelayedCallbacks = null 2059 2060 // Often, we want to signal events at a point where we are in the 2061 // middle of some work, but don't want the handler to start calling 2062 // other methods on the editor, which might be in an inconsistent 2063 // state or simply not expect any other events to happen. 2064 // signalLater looks whether there are any handlers, and schedules 2065 // them to be executed when the last operation ends, or, if no 2066 // operation is active, when a timeout fires. 2067 function signalLater(emitter, type /*, values...*/) { 2068 var arr = getHandlers(emitter, type) 2069 if (!arr.length) { return } 2070 var args = Array.prototype.slice.call(arguments, 2), list 2071 if (operationGroup) { 2072 list = operationGroup.delayedCallbacks 2073 } else if (orphanDelayedCallbacks) { 2074 list = orphanDelayedCallbacks 2075 } else { 2076 list = orphanDelayedCallbacks = [] 2077 setTimeout(fireOrphanDelayed, 0) 2078 } 2079 var loop = function ( i ) { 2080 list.push(function () { return arr[i].apply(null, args); }) 2081 }; 2082 2083 for (var i = 0; i < arr.length; ++i) 2084 loop( i ); 2085 } 2086 2087 function fireOrphanDelayed() { 2088 var delayed = orphanDelayedCallbacks 2089 orphanDelayedCallbacks = null 2090 for (var i = 0; i < delayed.length; ++i) { delayed[i]() } 2091 } 2092 2093 // When an aspect of a line changes, a string is added to 2094 // lineView.changes. This updates the relevant part of the line's 2095 // DOM structure. 2096 function updateLineForChanges(cm, lineView, lineN, dims) { 2097 for (var j = 0; j < lineView.changes.length; j++) { 2098 var type = lineView.changes[j] 2099 if (type == "text") { updateLineText(cm, lineView) } 2100 else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims) } 2101 else if (type == "class") { updateLineClasses(lineView) } 2102 else if (type == "widget") { updateLineWidgets(cm, lineView, dims) } 2103 } 2104 lineView.changes = null 2105 } 2106 2107 // Lines with gutter elements, widgets or a background class need to 2108 // be wrapped, and have the extra elements added to the wrapper div 2109 function ensureLineWrapped(lineView) { 2110 if (lineView.node == lineView.text) { 2111 lineView.node = elt("div", null, null, "position: relative") 2112 if (lineView.text.parentNode) 2113 { lineView.text.parentNode.replaceChild(lineView.node, lineView.text) } 2114 lineView.node.appendChild(lineView.text) 2115 if (ie && ie_version < 8) { lineView.node.style.zIndex = 2 } 2116 } 2117 return lineView.node 2118 } 2119 2120 function updateLineBackground(lineView) { 2121 var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass 2122 if (cls) { cls += " CodeMirror-linebackground" } 2123 if (lineView.background) { 2124 if (cls) { lineView.background.className = cls } 2125 else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null } 2126 } else if (cls) { 2127 var wrap = ensureLineWrapped(lineView) 2128 lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild) 2129 } 2130 } 2131 2132 // Wrapper around buildLineContent which will reuse the structure 2133 // in display.externalMeasured when possible. 2134 function getLineContent(cm, lineView) { 2135 var ext = cm.display.externalMeasured 2136 if (ext && ext.line == lineView.line) { 2137 cm.display.externalMeasured = null 2138 lineView.measure = ext.measure 2139 return ext.built 2140 } 2141 return buildLineContent(cm, lineView) 2142 } 2143 2144 // Redraw the line's text. Interacts with the background and text 2145 // classes because the mode may output tokens that influence these 2146 // classes. 2147 function updateLineText(cm, lineView) { 2148 var cls = lineView.text.className 2149 var built = getLineContent(cm, lineView) 2150 if (lineView.text == lineView.node) { lineView.node = built.pre } 2151 lineView.text.parentNode.replaceChild(built.pre, lineView.text) 2152 lineView.text = built.pre 2153 if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { 2154 lineView.bgClass = built.bgClass 2155 lineView.textClass = built.textClass 2156 updateLineClasses(lineView) 2157 } else if (cls) { 2158 lineView.text.className = cls 2159 } 2160 } 2161 2162 function updateLineClasses(lineView) { 2163 updateLineBackground(lineView) 2164 if (lineView.line.wrapClass) 2165 { ensureLineWrapped(lineView).className = lineView.line.wrapClass } 2166 else if (lineView.node != lineView.text) 2167 { lineView.node.className = "" } 2168 var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass 2169 lineView.text.className = textClass || "" 2170 } 2171 2172 function updateLineGutter(cm, lineView, lineN, dims) { 2173 if (lineView.gutter) { 2174 lineView.node.removeChild(lineView.gutter) 2175 lineView.gutter = null 2176 } 2177 if (lineView.gutterBackground) { 2178 lineView.node.removeChild(lineView.gutterBackground) 2179 lineView.gutterBackground = null 2180 } 2181 if (lineView.line.gutterClass) { 2182 var wrap = ensureLineWrapped(lineView) 2183 lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, 2184 ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")) 2185 wrap.insertBefore(lineView.gutterBackground, lineView.text) 2186 } 2187 var markers = lineView.line.gutterMarkers 2188 if (cm.options.lineNumbers || markers) { 2189 var wrap$1 = ensureLineWrapped(lineView) 2190 var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")) 2191 cm.display.input.setUneditable(gutterWrap) 2192 wrap$1.insertBefore(gutterWrap, lineView.text) 2193 if (lineView.line.gutterClass) 2194 { gutterWrap.className += " " + lineView.line.gutterClass } 2195 if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) 2196 { lineView.lineNumber = gutterWrap.appendChild( 2197 elt("div", lineNumberFor(cm.options, lineN), 2198 "CodeMirror-linenumber CodeMirror-gutter-elt", 2199 ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))) } 2200 if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) { 2201 var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id] 2202 if (found) 2203 { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", 2204 ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))) } 2205 } } 2206 } 2207 } 2208 2209 function updateLineWidgets(cm, lineView, dims) { 2210 if (lineView.alignable) { lineView.alignable = null } 2211 for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { 2212 next = node.nextSibling 2213 if (node.className == "CodeMirror-linewidget") 2214 { lineView.node.removeChild(node) } 2215 } 2216 insertLineWidgets(cm, lineView, dims) 2217 } 2218 2219 // Build a line's DOM representation from scratch 2220 function buildLineElement(cm, lineView, lineN, dims) { 2221 var built = getLineContent(cm, lineView) 2222 lineView.text = lineView.node = built.pre 2223 if (built.bgClass) { lineView.bgClass = built.bgClass } 2224 if (built.textClass) { lineView.textClass = built.textClass } 2225 2226 updateLineClasses(lineView) 2227 updateLineGutter(cm, lineView, lineN, dims) 2228 insertLineWidgets(cm, lineView, dims) 2229 return lineView.node 2230 } 2231 2232 // A lineView may contain multiple logical lines (when merged by 2233 // collapsed spans). The widgets for all of them need to be drawn. 2234 function insertLineWidgets(cm, lineView, dims) { 2235 insertLineWidgetsFor(cm, lineView.line, lineView, dims, true) 2236 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) 2237 { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } } 2238 } 2239 2240 function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { 2241 if (!line.widgets) { return } 2242 var wrap = ensureLineWrapped(lineView) 2243 for (var i = 0, ws = line.widgets; i < ws.length; ++i) { 2244 var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget") 2245 if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true") } 2246 positionLineWidget(widget, node, lineView, dims) 2247 cm.display.input.setUneditable(node) 2248 if (allowAbove && widget.above) 2249 { wrap.insertBefore(node, lineView.gutter || lineView.text) } 2250 else 2251 { wrap.appendChild(node) } 2252 signalLater(widget, "redraw") 2253 } 2254 } 2255 2256 function positionLineWidget(widget, node, lineView, dims) { 2257 if (widget.noHScroll) { 2258 ;(lineView.alignable || (lineView.alignable = [])).push(node) 2259 var width = dims.wrapperWidth 2260 node.style.left = dims.fixedPos + "px" 2261 if (!widget.coverGutter) { 2262 width -= dims.gutterTotalWidth 2263 node.style.paddingLeft = dims.gutterTotalWidth + "px" 2264 } 2265 node.style.width = width + "px" 2266 } 2267 if (widget.coverGutter) { 2268 node.style.zIndex = 5 2269 node.style.position = "relative" 2270 if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px" } 2271 } 2272 } 2273 2274 function widgetHeight(widget) { 2275 if (widget.height != null) { return widget.height } 2276 var cm = widget.doc.cm 2277 if (!cm) { return 0 } 2278 if (!contains(document.body, widget.node)) { 2279 var parentStyle = "position: relative;" 2280 if (widget.coverGutter) 2281 { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" } 2282 if (widget.noHScroll) 2283 { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" } 2284 removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)) 2285 } 2286 return widget.height = widget.node.parentNode.offsetHeight 2287 } 2288 2289 // Return true when the given mouse event happened in a widget 2290 function eventInWidget(display, e) { 2291 for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { 2292 if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || 2293 (n.parentNode == display.sizer && n != display.mover)) 2294 { return true } 2295 } 2296 } 2297 2298 // POSITION MEASUREMENT 2299 2300 function paddingTop(display) {return display.lineSpace.offsetTop} 2301 function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} 2302 function paddingH(display) { 2303 if (display.cachedPaddingH) { return display.cachedPaddingH } 2304 var e = removeChildrenAndAdd(display.measure, elt("pre", "x")) 2305 var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle 2306 var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} 2307 if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data } 2308 return data 2309 } 2310 2311 function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } 2312 function displayWidth(cm) { 2313 return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth 2314 } 2315 function displayHeight(cm) { 2316 return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight 2317 } 2318 2319 // Ensure the lineView.wrapping.heights array is populated. This is 2320 // an array of bottom offsets for the lines that make up a drawn 2321 // line. When lineWrapping is on, there might be more than one 2322 // height. 2323 function ensureLineHeights(cm, lineView, rect) { 2324 var wrapping = cm.options.lineWrapping 2325 var curWidth = wrapping && displayWidth(cm) 2326 if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { 2327 var heights = lineView.measure.heights = [] 2328 if (wrapping) { 2329 lineView.measure.width = curWidth 2330 var rects = lineView.text.firstChild.getClientRects() 2331 for (var i = 0; i < rects.length - 1; i++) { 2332 var cur = rects[i], next = rects[i + 1] 2333 if (Math.abs(cur.bottom - next.bottom) > 2) 2334 { heights.push((cur.bottom + next.top) / 2 - rect.top) } 2335 } 2336 } 2337 heights.push(rect.bottom - rect.top) 2338 } 2339 } 2340 2341 // Find a line map (mapping character offsets to text nodes) and a 2342 // measurement cache for the given line number. (A line view might 2343 // contain multiple lines when collapsed ranges are present.) 2344 function mapFromLineView(lineView, line, lineN) { 2345 if (lineView.line == line) 2346 { return {map: lineView.measure.map, cache: lineView.measure.cache} } 2347 for (var i = 0; i < lineView.rest.length; i++) 2348 { if (lineView.rest[i] == line) 2349 { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } 2350 for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) 2351 { if (lineNo(lineView.rest[i$1]) > lineN) 2352 { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } 2353 } 2354 2355 // Render a line into the hidden node display.externalMeasured. Used 2356 // when measurement is needed for a line that's not in the viewport. 2357 function updateExternalMeasurement(cm, line) { 2358 line = visualLine(line) 2359 var lineN = lineNo(line) 2360 var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN) 2361 view.lineN = lineN 2362 var built = view.built = buildLineContent(cm, view) 2363 view.text = built.pre 2364 removeChildrenAndAdd(cm.display.lineMeasure, built.pre) 2365 return view 2366 } 2367 2368 // Get a {top, bottom, left, right} box (in line-local coordinates) 2369 // for a given character. 2370 function measureChar(cm, line, ch, bias) { 2371 return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) 2372 } 2373 2374 // Find a line view that corresponds to the given line number. 2375 function findViewForLine(cm, lineN) { 2376 if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) 2377 { return cm.display.view[findViewIndex(cm, lineN)] } 2378 var ext = cm.display.externalMeasured 2379 if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) 2380 { return ext } 2381 } 2382 2383 // Measurement can be split in two steps, the set-up work that 2384 // applies to the whole line, and the measurement of the actual 2385 // character. Functions like coordsChar, that need to do a lot of 2386 // measurements in a row, can thus ensure that the set-up work is 2387 // only done once. 2388 function prepareMeasureForLine(cm, line) { 2389 var lineN = lineNo(line) 2390 var view = findViewForLine(cm, lineN) 2391 if (view && !view.text) { 2392 view = null 2393 } else if (view && view.changes) { 2394 updateLineForChanges(cm, view, lineN, getDimensions(cm)) 2395 cm.curOp.forceUpdate = true 2396 } 2397 if (!view) 2398 { view = updateExternalMeasurement(cm, line) } 2399 2400 var info = mapFromLineView(view, line, lineN) 2401 return { 2402 line: line, view: view, rect: null, 2403 map: info.map, cache: info.cache, before: info.before, 2404 hasHeights: false 2405 } 2406 } 2407 2408 // Given a prepared measurement object, measures the position of an 2409 // actual character (or fetches it from the cache). 2410 function measureCharPrepared(cm, prepared, ch, bias, varHeight) { 2411 if (prepared.before) { ch = -1 } 2412 var key = ch + (bias || ""), found 2413 if (prepared.cache.hasOwnProperty(key)) { 2414 found = prepared.cache[key] 2415 } else { 2416 if (!prepared.rect) 2417 { prepared.rect = prepared.view.text.getBoundingClientRect() } 2418 if (!prepared.hasHeights) { 2419 ensureLineHeights(cm, prepared.view, prepared.rect) 2420 prepared.hasHeights = true 2421 } 2422 found = measureCharInner(cm, prepared, ch, bias) 2423 if (!found.bogus) { prepared.cache[key] = found } 2424 } 2425 return {left: found.left, right: found.right, 2426 top: varHeight ? found.rtop : found.top, 2427 bottom: varHeight ? found.rbottom : found.bottom} 2428 } 2429 2430 var nullRect = {left: 0, right: 0, top: 0, bottom: 0} 2431 2432 function nodeAndOffsetInLineMap(map, ch, bias) { 2433 var node, start, end, collapse, mStart, mEnd 2434 // First, search the line map for the text node corresponding to, 2435 // or closest to, the target character. 2436 for (var i = 0; i < map.length; i += 3) { 2437 mStart = map[i] 2438 mEnd = map[i + 1] 2439 if (ch < mStart) { 2440 start = 0; end = 1 2441 collapse = "left" 2442 } else if (ch < mEnd) { 2443 start = ch - mStart 2444 end = start + 1 2445 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { 2446 end = mEnd - mStart 2447 start = end - 1 2448 if (ch >= mEnd) { collapse = "right" } 2449 } 2450 if (start != null) { 2451 node = map[i + 2] 2452 if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) 2453 { collapse = bias } 2454 if (bias == "left" && start == 0) 2455 { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { 2456 node = map[(i -= 3) + 2] 2457 collapse = "left" 2458 } } 2459 if (bias == "right" && start == mEnd - mStart) 2460 { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { 2461 node = map[(i += 3) + 2] 2462 collapse = "right" 2463 } } 2464 break 2465 } 2466 } 2467 return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} 2468 } 2469 2470 function getUsefulRect(rects, bias) { 2471 var rect = nullRect 2472 if (bias == "left") { for (var i = 0; i < rects.length; i++) { 2473 if ((rect = rects[i]).left != rect.right) { break } 2474 } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { 2475 if ((rect = rects[i$1]).left != rect.right) { break } 2476 } } 2477 return rect 2478 } 2479 2480 function measureCharInner(cm, prepared, ch, bias) { 2481 var place = nodeAndOffsetInLineMap(prepared.map, ch, bias) 2482 var node = place.node, start = place.start, end = place.end, collapse = place.collapse 2483 2484 var rect 2485 if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. 2486 for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned 2487 while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start } 2488 while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end } 2489 if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) 2490 { rect = node.parentNode.getBoundingClientRect() } 2491 else 2492 { rect = getUsefulRect(range(node, start, end).getClientRects(), bias) } 2493 if (rect.left || rect.right || start == 0) { break } 2494 end = start 2495 start = start - 1 2496 collapse = "right" 2497 } 2498 if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect) } 2499 } else { // If it is a widget, simply get the box for the whole widget. 2500 if (start > 0) { collapse = bias = "right" } 2501 var rects 2502 if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) 2503 { rect = rects[bias == "right" ? rects.length - 1 : 0] } 2504 else 2505 { rect = node.getBoundingClientRect() } 2506 } 2507 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { 2508 var rSpan = node.parentNode.getClientRects()[0] 2509 if (rSpan) 2510 { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} } 2511 else 2512 { rect = nullRect } 2513 } 2514 2515 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top 2516 var mid = (rtop + rbot) / 2 2517 var heights = prepared.view.measure.heights 2518 var i = 0 2519 for (; i < heights.length - 1; i++) 2520 { if (mid < heights[i]) { break } } 2521 var top = i ? heights[i - 1] : 0, bot = heights[i] 2522 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, 2523 right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, 2524 top: top, bottom: bot} 2525 if (!rect.left && !rect.right) { result.bogus = true } 2526 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot } 2527 2528 return result 2529 } 2530 2531 // Work around problem with bounding client rects on ranges being 2532 // returned incorrectly when zoomed on IE10 and below. 2533 function maybeUpdateRectForZooming(measure, rect) { 2534 if (!window.screen || screen.logicalXDPI == null || 2535 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) 2536 { return rect } 2537 var scaleX = screen.logicalXDPI / screen.deviceXDPI 2538 var scaleY = screen.logicalYDPI / screen.deviceYDPI 2539 return {left: rect.left * scaleX, right: rect.right * scaleX, 2540 top: rect.top * scaleY, bottom: rect.bottom * scaleY} 2541 } 2542 2543 function clearLineMeasurementCacheFor(lineView) { 2544 if (lineView.measure) { 2545 lineView.measure.cache = {} 2546 lineView.measure.heights = null 2547 if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) 2548 { lineView.measure.caches[i] = {} } } 2549 } 2550 } 2551 2552 function clearLineMeasurementCache(cm) { 2553 cm.display.externalMeasure = null 2554 removeChildren(cm.display.lineMeasure) 2555 for (var i = 0; i < cm.display.view.length; i++) 2556 { clearLineMeasurementCacheFor(cm.display.view[i]) } 2557 } 2558 2559 function clearCaches(cm) { 2560 clearLineMeasurementCache(cm) 2561 cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null 2562 if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true } 2563 cm.display.lineNumChars = null 2564 } 2565 2566 function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft } 2567 function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop } 2568 2569 // Converts a {top, bottom, left, right} box from line-local 2570 // coordinates into another coordinate system. Context may be one of 2571 // "line", "div" (display.lineDiv), "local"./null (editor), "window", 2572 // or "page". 2573 function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { 2574 if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) { 2575 var size = widgetHeight(lineObj.widgets[i]) 2576 rect.top += size; rect.bottom += size 2577 } } } 2578 if (context == "line") { return rect } 2579 if (!context) { context = "local" } 2580 var yOff = heightAtLine(lineObj) 2581 if (context == "local") { yOff += paddingTop(cm.display) } 2582 else { yOff -= cm.display.viewOffset } 2583 if (context == "page" || context == "window") { 2584 var lOff = cm.display.lineSpace.getBoundingClientRect() 2585 yOff += lOff.top + (context == "window" ? 0 : pageScrollY()) 2586 var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()) 2587 rect.left += xOff; rect.right += xOff 2588 } 2589 rect.top += yOff; rect.bottom += yOff 2590 return rect 2591 } 2592 2593 // Coverts a box from "div" coords to another coordinate system. 2594 // Context may be "window", "page", "div", or "local"./null. 2595 function fromCoordSystem(cm, coords, context) { 2596 if (context == "div") { return coords } 2597 var left = coords.left, top = coords.top 2598 // First move into "page" coordinate system 2599 if (context == "page") { 2600 left -= pageScrollX() 2601 top -= pageScrollY() 2602 } else if (context == "local" || !context) { 2603 var localBox = cm.display.sizer.getBoundingClientRect() 2604 left += localBox.left 2605 top += localBox.top 2606 } 2607 2608 var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect() 2609 return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} 2610 } 2611 2612 function charCoords(cm, pos, context, lineObj, bias) { 2613 if (!lineObj) { lineObj = getLine(cm.doc, pos.line) } 2614 return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) 2615 } 2616 2617 // Returns a box for a given cursor position, which may have an 2618 // 'other' property containing the position of the secondary cursor 2619 // on a bidi boundary. 2620 function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { 2621 lineObj = lineObj || getLine(cm.doc, pos.line) 2622 if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) } 2623 function get(ch, right) { 2624 var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight) 2625 if (right) { m.left = m.right; } else { m.right = m.left } 2626 return intoCoordSystem(cm, lineObj, m, context) 2627 } 2628 function getBidi(ch, partPos) { 2629 var part = order[partPos], right = part.level % 2 2630 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { 2631 part = order[--partPos] 2632 ch = bidiRight(part) - (part.level % 2 ? 0 : 1) 2633 right = true 2634 } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) { 2635 part = order[++partPos] 2636 ch = bidiLeft(part) - part.level % 2 2637 right = false 2638 } 2639 if (right && ch == part.to && ch > part.from) { return get(ch - 1) } 2640 return get(ch, right) 2641 } 2642 var order = getOrder(lineObj), ch = pos.ch 2643 if (!order) { return get(ch) } 2644 var partPos = getBidiPartAt(order, ch) 2645 var val = getBidi(ch, partPos) 2646 if (bidiOther != null) { val.other = getBidi(ch, bidiOther) } 2647 return val 2648 } 2649 2650 // Used to cheaply estimate the coordinates for a position. Used for 2651 // intermediate scroll updates. 2652 function estimateCoords(cm, pos) { 2653 var left = 0 2654 pos = clipPos(cm.doc, pos) 2655 if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch } 2656 var lineObj = getLine(cm.doc, pos.line) 2657 var top = heightAtLine(lineObj) + paddingTop(cm.display) 2658 return {left: left, right: left, top: top, bottom: top + lineObj.height} 2659 } 2660 2661 // Positions returned by coordsChar contain some extra information. 2662 // xRel is the relative x position of the input coordinates compared 2663 // to the found position (so xRel > 0 means the coordinates are to 2664 // the right of the character position, for example). When outside 2665 // is true, that means the coordinates lie outside the line's 2666 // vertical range. 2667 function PosWithInfo(line, ch, outside, xRel) { 2668 var pos = Pos(line, ch) 2669 pos.xRel = xRel 2670 if (outside) { pos.outside = true } 2671 return pos 2672 } 2673 2674 // Compute the character position closest to the given coordinates. 2675 // Input must be lineSpace-local ("div" coordinate system). 2676 function coordsChar(cm, x, y) { 2677 var doc = cm.doc 2678 y += cm.display.viewOffset 2679 if (y < 0) { return PosWithInfo(doc.first, 0, true, -1) } 2680 var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1 2681 if (lineN > last) 2682 { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) } 2683 if (x < 0) { x = 0 } 2684 2685 var lineObj = getLine(doc, lineN) 2686 for (;;) { 2687 var found = coordsCharInner(cm, lineObj, lineN, x, y) 2688 var merged = collapsedSpanAtEnd(lineObj) 2689 var mergedPos = merged && merged.find(0, true) 2690 if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) 2691 { lineN = lineNo(lineObj = mergedPos.to.line) } 2692 else 2693 { return found } 2694 } 2695 } 2696 2697 function coordsCharInner(cm, lineObj, lineNo, x, y) { 2698 var innerOff = y - heightAtLine(lineObj) 2699 var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth 2700 var preparedMeasure = prepareMeasureForLine(cm, lineObj) 2701 2702 function getX(ch) { 2703 var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure) 2704 wrongLine = true 2705 if (innerOff > sp.bottom) { return sp.left - adjust } 2706 else if (innerOff < sp.top) { return sp.left + adjust } 2707 else { wrongLine = false } 2708 return sp.left 2709 } 2710 2711 var bidi = getOrder(lineObj), dist = lineObj.text.length 2712 var from = lineLeft(lineObj), to = lineRight(lineObj) 2713 var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine 2714 2715 if (x > toX) { return PosWithInfo(lineNo, to, toOutside, 1) } 2716 // Do a binary search between these bounds. 2717 for (;;) { 2718 if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { 2719 var ch = x < fromX || x - fromX <= toX - x ? from : to 2720 var outside = ch == from ? fromOutside : toOutside 2721 var xDiff = x - (ch == from ? fromX : toX) 2722 // This is a kludge to handle the case where the coordinates 2723 // are after a line-wrapped line. We should replace it with a 2724 // more general handling of cursor positions around line 2725 // breaks. (Issue #4078) 2726 if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 && 2727 ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) { 2728 var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right") 2729 if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) { 2730 outside = false 2731 ch++ 2732 xDiff = x - charSize.right 2733 } 2734 } 2735 while (isExtendingChar(lineObj.text.charAt(ch))) { ++ch } 2736 var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0) 2737 return pos 2738 } 2739 var step = Math.ceil(dist / 2), middle = from + step 2740 if (bidi) { 2741 middle = from 2742 for (var i = 0; i < step; ++i) { middle = moveVisually(lineObj, middle, 1) } 2743 } 2744 var middleX = getX(middle) 2745 if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) { toX += 1000; } dist = step} 2746 else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step} 2747 } 2748 } 2749 2750 var measureText 2751 // Compute the default text height. 2752 function textHeight(display) { 2753 if (display.cachedTextHeight != null) { return display.cachedTextHeight } 2754 if (measureText == null) { 2755 measureText = elt("pre") 2756 // Measure a bunch of lines, for browsers that compute 2757 // fractional heights. 2758 for (var i = 0; i < 49; ++i) { 2759 measureText.appendChild(document.createTextNode("x")) 2760 measureText.appendChild(elt("br")) 2761 } 2762 measureText.appendChild(document.createTextNode("x")) 2763 } 2764 removeChildrenAndAdd(display.measure, measureText) 2765 var height = measureText.offsetHeight / 50 2766 if (height > 3) { display.cachedTextHeight = height } 2767 removeChildren(display.measure) 2768 return height || 1 2769 } 2770 2771 // Compute the default character width. 2772 function charWidth(display) { 2773 if (display.cachedCharWidth != null) { return display.cachedCharWidth } 2774 var anchor = elt("span", "xxxxxxxxxx") 2775 var pre = elt("pre", [anchor]) 2776 removeChildrenAndAdd(display.measure, pre) 2777 var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 2778 if (width > 2) { display.cachedCharWidth = width } 2779 return width || 10 2780 } 2781 2782 // Do a bulk-read of the DOM positions and sizes needed to draw the 2783 // view, so that we don't interleave reading and writing to the DOM. 2784 function getDimensions(cm) { 2785 var d = cm.display, left = {}, width = {} 2786 var gutterLeft = d.gutters.clientLeft 2787 for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { 2788 left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft 2789 width[cm.options.gutters[i]] = n.clientWidth 2790 } 2791 return {fixedPos: compensateForHScroll(d), 2792 gutterTotalWidth: d.gutters.offsetWidth, 2793 gutterLeft: left, 2794 gutterWidth: width, 2795 wrapperWidth: d.wrapper.clientWidth} 2796 } 2797 2798 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, 2799 // but using getBoundingClientRect to get a sub-pixel-accurate 2800 // result. 2801 function compensateForHScroll(display) { 2802 return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left 2803 } 2804 2805 // Returns a function that estimates the height of a line, to use as 2806 // first approximation until the line becomes visible (and is thus 2807 // properly measurable). 2808 function estimateHeight(cm) { 2809 var th = textHeight(cm.display), wrapping = cm.options.lineWrapping 2810 var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3) 2811 return function (line) { 2812 if (lineIsHidden(cm.doc, line)) { return 0 } 2813 2814 var widgetsHeight = 0 2815 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { 2816 if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height } 2817 } } 2818 2819 if (wrapping) 2820 { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } 2821 else 2822 { return widgetsHeight + th } 2823 } 2824 } 2825 2826 function estimateLineHeights(cm) { 2827 var doc = cm.doc, est = estimateHeight(cm) 2828 doc.iter(function (line) { 2829 var estHeight = est(line) 2830 if (estHeight != line.height) { updateLineHeight(line, estHeight) } 2831 }) 2832 } 2833 2834 // Given a mouse event, find the corresponding position. If liberal 2835 // is false, it checks whether a gutter or scrollbar was clicked, 2836 // and returns null if it was. forRect is used by rectangular 2837 // selections, and tries to estimate a character position even for 2838 // coordinates beyond the right of the text. 2839 function posFromMouse(cm, e, liberal, forRect) { 2840 var display = cm.display 2841 if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } 2842 2843 var x, y, space = display.lineSpace.getBoundingClientRect() 2844 // Fails unpredictably on IE[67] when mouse is dragged around quickly. 2845 try { x = e.clientX - space.left; y = e.clientY - space.top } 2846 catch (e) { return null } 2847 var coords = coordsChar(cm, x, y), line 2848 if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { 2849 var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length 2850 coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)) 2851 } 2852 return coords 2853 } 2854 2855 // Find the view element corresponding to a given line. Return null 2856 // when the line isn't visible. 2857 function findViewIndex(cm, n) { 2858 if (n >= cm.display.viewTo) { return null } 2859 n -= cm.display.viewFrom 2860 if (n < 0) { return null } 2861 var view = cm.display.view 2862 for (var i = 0; i < view.length; i++) { 2863 n -= view[i].size 2864 if (n < 0) { return i } 2865 } 2866 } 2867 2868 function updateSelection(cm) { 2869 cm.display.input.showSelection(cm.display.input.prepareSelection()) 2870 } 2871 2872 function prepareSelection(cm, primary) { 2873 var doc = cm.doc, result = {} 2874 var curFragment = result.cursors = document.createDocumentFragment() 2875 var selFragment = result.selection = document.createDocumentFragment() 2876 2877 for (var i = 0; i < doc.sel.ranges.length; i++) { 2878 if (primary === false && i == doc.sel.primIndex) { continue } 2879 var range = doc.sel.ranges[i] 2880 if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } 2881 var collapsed = range.empty() 2882 if (collapsed || cm.options.showCursorWhenSelecting) 2883 { drawSelectionCursor(cm, range.head, curFragment) } 2884 if (!collapsed) 2885 { drawSelectionRange(cm, range, selFragment) } 2886 } 2887 return result 2888 } 2889 2890 // Draws a cursor for the given range 2891 function drawSelectionCursor(cm, head, output) { 2892 var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine) 2893 2894 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")) 2895 cursor.style.left = pos.left + "px" 2896 cursor.style.top = pos.top + "px" 2897 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px" 2898 2899 if (pos.other) { 2900 // Secondary cursor, shown when on a 'jump' in bi-directional text 2901 var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")) 2902 otherCursor.style.display = "" 2903 otherCursor.style.left = pos.other.left + "px" 2904 otherCursor.style.top = pos.other.top + "px" 2905 otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px" 2906 } 2907 } 2908 2909 // Draws the given range as a highlighted selection 2910 function drawSelectionRange(cm, range, output) { 2911 var display = cm.display, doc = cm.doc 2912 var fragment = document.createDocumentFragment() 2913 var padding = paddingH(cm.display), leftSide = padding.left 2914 var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right 2915 2916 function add(left, top, width, bottom) { 2917 if (top < 0) { top = 0 } 2918 top = Math.round(top) 2919 bottom = Math.round(bottom) 2920 fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))) 2921 } 2922 2923 function drawForLine(line, fromArg, toArg) { 2924 var lineObj = getLine(doc, line) 2925 var lineLen = lineObj.text.length 2926 var start, end 2927 function coords(ch, bias) { 2928 return charCoords(cm, Pos(line, ch), "div", lineObj, bias) 2929 } 2930 2931 iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) { 2932 var leftPos = coords(from, "left"), rightPos, left, right 2933 if (from == to) { 2934 rightPos = leftPos 2935 left = right = leftPos.left 2936 } else { 2937 rightPos = coords(to - 1, "right") 2938 if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp } 2939 left = leftPos.left 2940 right = rightPos.right 2941 } 2942 if (fromArg == null && from == 0) { left = leftSide } 2943 if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part 2944 add(left, leftPos.top, null, leftPos.bottom) 2945 left = leftSide 2946 if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) } 2947 } 2948 if (toArg == null && to == lineLen) { right = rightSide } 2949 if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) 2950 { start = leftPos } 2951 if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) 2952 { end = rightPos } 2953 if (left < leftSide + 1) { left = leftSide } 2954 add(left, rightPos.top, right - left, rightPos.bottom) 2955 }) 2956 return {start: start, end: end} 2957 } 2958 2959 var sFrom = range.from(), sTo = range.to() 2960 if (sFrom.line == sTo.line) { 2961 drawForLine(sFrom.line, sFrom.ch, sTo.ch) 2962 } else { 2963 var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line) 2964 var singleVLine = visualLine(fromLine) == visualLine(toLine) 2965 var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end 2966 var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start 2967 if (singleVLine) { 2968 if (leftEnd.top < rightStart.top - 2) { 2969 add(leftEnd.right, leftEnd.top, null, leftEnd.bottom) 2970 add(leftSide, rightStart.top, rightStart.left, rightStart.bottom) 2971 } else { 2972 add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom) 2973 } 2974 } 2975 if (leftEnd.bottom < rightStart.top) 2976 { add(leftSide, leftEnd.bottom, null, rightStart.top) } 2977 } 2978 2979 output.appendChild(fragment) 2980 } 2981 2982 // Cursor-blinking 2983 function restartBlink(cm) { 2984 if (!cm.state.focused) { return } 2985 var display = cm.display 2986 clearInterval(display.blinker) 2987 var on = true 2988 display.cursorDiv.style.visibility = "" 2989 if (cm.options.cursorBlinkRate > 0) 2990 { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, 2991 cm.options.cursorBlinkRate) } 2992 else if (cm.options.cursorBlinkRate < 0) 2993 { display.cursorDiv.style.visibility = "hidden" } 2994 } 2995 2996 function ensureFocus(cm) { 2997 if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) } 2998 } 2999 3000 function delayBlurEvent(cm) { 3001 cm.state.delayingBlurEvent = true 3002 setTimeout(function () { if (cm.state.delayingBlurEvent) { 3003 cm.state.delayingBlurEvent = false 3004 onBlur(cm) 3005 } }, 100) 3006 } 3007 3008 function onFocus(cm, e) { 3009 if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false } 3010 3011 if (cm.options.readOnly == "nocursor") { return } 3012 if (!cm.state.focused) { 3013 signal(cm, "focus", cm, e) 3014 cm.state.focused = true 3015 addClass(cm.display.wrapper, "CodeMirror-focused") 3016 // This test prevents this from firing when a context 3017 // menu is closed (since the input reset would kill the 3018 // select-all detection hack) 3019 if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { 3020 cm.display.input.reset() 3021 if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20) } // Issue #1730 3022 } 3023 cm.display.input.receivedFocus() 3024 } 3025 restartBlink(cm) 3026 } 3027 function onBlur(cm, e) { 3028 if (cm.state.delayingBlurEvent) { return } 3029 3030 if (cm.state.focused) { 3031 signal(cm, "blur", cm, e) 3032 cm.state.focused = false 3033 rmClass(cm.display.wrapper, "CodeMirror-focused") 3034 } 3035 clearInterval(cm.display.blinker) 3036 setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false } }, 150) 3037 } 3038 3039 // Re-align line numbers and gutter marks to compensate for 3040 // horizontal scrolling. 3041 function alignHorizontally(cm) { 3042 var display = cm.display, view = display.view 3043 if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } 3044 var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft 3045 var gutterW = display.gutters.offsetWidth, left = comp + "px" 3046 for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { 3047 if (cm.options.fixedGutter) { 3048 if (view[i].gutter) 3049 { view[i].gutter.style.left = left } 3050 if (view[i].gutterBackground) 3051 { view[i].gutterBackground.style.left = left } 3052 } 3053 var align = view[i].alignable 3054 if (align) { for (var j = 0; j < align.length; j++) 3055 { align[j].style.left = left } } 3056 } } 3057 if (cm.options.fixedGutter) 3058 { display.gutters.style.left = (comp + gutterW) + "px" } 3059 } 3060 3061 // Used to ensure that the line number gutter is still the right 3062 // size for the current document size. Returns true when an update 3063 // is needed. 3064 function maybeUpdateLineNumberWidth(cm) { 3065 if (!cm.options.lineNumbers) { return false } 3066 var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display 3067 if (last.length != display.lineNumChars) { 3068 var test = display.measure.appendChild(elt("div", [elt("div", last)], 3069 "CodeMirror-linenumber CodeMirror-gutter-elt")) 3070 var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW 3071 display.lineGutter.style.width = "" 3072 display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1 3073 display.lineNumWidth = display.lineNumInnerWidth + padding 3074 display.lineNumChars = display.lineNumInnerWidth ? last.length : -1 3075 display.lineGutter.style.width = display.lineNumWidth + "px" 3076 updateGutterSpace(cm) 3077 return true 3078 } 3079 return false 3080 } 3081 3082 // Read the actual heights of the rendered lines, and update their 3083 // stored heights to match. 3084 function updateHeightsInViewport(cm) { 3085 var display = cm.display 3086 var prevBottom = display.lineDiv.offsetTop 3087 for (var i = 0; i < display.view.length; i++) { 3088 var cur = display.view[i], height = (void 0) 3089 if (cur.hidden) { continue } 3090 if (ie && ie_version < 8) { 3091 var bot = cur.node.offsetTop + cur.node.offsetHeight 3092 height = bot - prevBottom 3093 prevBottom = bot 3094 } else { 3095 var box = cur.node.getBoundingClientRect() 3096 height = box.bottom - box.top 3097 } 3098 var diff = cur.line.height - height 3099 if (height < 2) { height = textHeight(display) } 3100 if (diff > .001 || diff < -.001) { 3101 updateLineHeight(cur.line, height) 3102 updateWidgetHeight(cur.line) 3103 if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) 3104 { updateWidgetHeight(cur.rest[j]) } } 3105 } 3106 } 3107 } 3108 3109 // Read and store the height of line widgets associated with the 3110 // given line. 3111 function updateWidgetHeight(line) { 3112 if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) 3113 { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } } 3114 } 3115 3116 // Compute the lines that are visible in a given viewport (defaults 3117 // the the current scroll position). viewport may contain top, 3118 // height, and ensure (see op.scrollToPos) properties. 3119 function visibleLines(display, doc, viewport) { 3120 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop 3121 top = Math.floor(top - paddingTop(display)) 3122 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight 3123 3124 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom) 3125 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and 3126 // forces those lines into the viewport (if possible). 3127 if (viewport && viewport.ensure) { 3128 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line 3129 if (ensureFrom < from) { 3130 from = ensureFrom 3131 to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight) 3132 } else if (Math.min(ensureTo, doc.lastLine()) >= to) { 3133 from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight) 3134 to = ensureTo 3135 } 3136 } 3137 return {from: from, to: Math.max(to, from + 1)} 3138 } 3139 3140 // Sync the scrollable area and scrollbars, ensure the viewport 3141 // covers the visible area. 3142 function setScrollTop(cm, val) { 3143 if (Math.abs(cm.doc.scrollTop - val) < 2) { return } 3144 cm.doc.scrollTop = val 3145 if (!gecko) { updateDisplaySimple(cm, {top: val}) } 3146 if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val } 3147 cm.display.scrollbars.setScrollTop(val) 3148 if (gecko) { updateDisplaySimple(cm) } 3149 startWorker(cm, 100) 3150 } 3151 // Sync scroller and scrollbar, ensure the gutter elements are 3152 // aligned. 3153 function setScrollLeft(cm, val, isScroller) { 3154 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) { return } 3155 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth) 3156 cm.doc.scrollLeft = val 3157 alignHorizontally(cm) 3158 if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val } 3159 cm.display.scrollbars.setScrollLeft(val) 3160 } 3161 3162 // Since the delta values reported on mouse wheel events are 3163 // unstandardized between browsers and even browser versions, and 3164 // generally horribly unpredictable, this code starts by measuring 3165 // the scroll effect that the first few mouse wheel events have, 3166 // and, from that, detects the way it can convert deltas to pixel 3167 // offsets afterwards. 3168 // 3169 // The reason we want to know the amount a wheel event will scroll 3170 // is that it gives us a chance to update the display before the 3171 // actual scrolling happens, reducing flickering. 3172 3173 var wheelSamples = 0; 3174 var wheelPixelsPerUnit = null; 3175 // Fill in a browser-detected starting value on browsers where we 3176 // know one. These don't have to be accurate -- the result of them 3177 // being wrong would just be a slight flicker on the first wheel 3178 // scroll (if it is large enough). 3179 if (ie) { wheelPixelsPerUnit = -.53 } 3180 else if (gecko) { wheelPixelsPerUnit = 15 } 3181 else if (chrome) { wheelPixelsPerUnit = -.7 } 3182 else if (safari) { wheelPixelsPerUnit = -1/3 } 3183 3184 function wheelEventDelta(e) { 3185 var dx = e.wheelDeltaX, dy = e.wheelDeltaY 3186 if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail } 3187 if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail } 3188 else if (dy == null) { dy = e.wheelDelta } 3189 return {x: dx, y: dy} 3190 } 3191 function wheelEventPixels(e) { 3192 var delta = wheelEventDelta(e) 3193 delta.x *= wheelPixelsPerUnit 3194 delta.y *= wheelPixelsPerUnit 3195 return delta 3196 } 3197 3198 function onScrollWheel(cm, e) { 3199 var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y 3200 3201 var display = cm.display, scroll = display.scroller 3202 // Quit if there's nothing to scroll here 3203 var canScrollX = scroll.scrollWidth > scroll.clientWidth 3204 var canScrollY = scroll.scrollHeight > scroll.clientHeight 3205 if (!(dx && canScrollX || dy && canScrollY)) { return } 3206 3207 // Webkit browsers on OS X abort momentum scrolls when the target 3208 // of the scroll event is removed from the scrollable element. 3209 // This hack (see related code in patchDisplay) makes sure the 3210 // element is kept around. 3211 if (dy && mac && webkit) { 3212 outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { 3213 for (var i = 0; i < view.length; i++) { 3214 if (view[i].node == cur) { 3215 cm.display.currentWheelTarget = cur 3216 break outer 3217 } 3218 } 3219 } 3220 } 3221 3222 // On some browsers, horizontal scrolling will cause redraws to 3223 // happen before the gutter has been realigned, causing it to 3224 // wriggle around in a most unseemly way. When we have an 3225 // estimated pixels/delta value, we just handle horizontal 3226 // scrolling entirely here. It'll be slightly off from native, but 3227 // better than glitching out. 3228 if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { 3229 if (dy && canScrollY) 3230 { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))) } 3231 setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth))) 3232 // Only prevent default scrolling if vertical scrolling is 3233 // actually possible. Otherwise, it causes vertical scroll 3234 // jitter on OSX trackpads when deltaX is small and deltaY 3235 // is large (issue #3579) 3236 if (!dy || (dy && canScrollY)) 3237 { e_preventDefault(e) } 3238 display.wheelStartX = null // Abort measurement, if in progress 3239 return 3240 } 3241 3242 // 'Project' the visible viewport to cover the area that is being 3243 // scrolled into view (if we know enough to estimate it). 3244 if (dy && wheelPixelsPerUnit != null) { 3245 var pixels = dy * wheelPixelsPerUnit 3246 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight 3247 if (pixels < 0) { top = Math.max(0, top + pixels - 50) } 3248 else { bot = Math.min(cm.doc.height, bot + pixels + 50) } 3249 updateDisplaySimple(cm, {top: top, bottom: bot}) 3250 } 3251 3252 if (wheelSamples < 20) { 3253 if (display.wheelStartX == null) { 3254 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop 3255 display.wheelDX = dx; display.wheelDY = dy 3256 setTimeout(function () { 3257 if (display.wheelStartX == null) { return } 3258 var movedX = scroll.scrollLeft - display.wheelStartX 3259 var movedY = scroll.scrollTop - display.wheelStartY 3260 var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || 3261 (movedX && display.wheelDX && movedX / display.wheelDX) 3262 display.wheelStartX = display.wheelStartY = null 3263 if (!sample) { return } 3264 wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1) 3265 ++wheelSamples 3266 }, 200) 3267 } else { 3268 display.wheelDX += dx; display.wheelDY += dy 3269 } 3270 } 3271 } 3272 3273 // SCROLLBARS 3274 3275 // Prepare DOM reads needed to update the scrollbars. Done in one 3276 // shot to minimize update/measure roundtrips. 3277 function measureForScrollbars(cm) { 3278 var d = cm.display, gutterW = d.gutters.offsetWidth 3279 var docH = Math.round(cm.doc.height + paddingVert(cm.display)) 3280 return { 3281 clientHeight: d.scroller.clientHeight, 3282 viewHeight: d.wrapper.clientHeight, 3283 scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, 3284 viewWidth: d.wrapper.clientWidth, 3285 barLeft: cm.options.fixedGutter ? gutterW : 0, 3286 docHeight: docH, 3287 scrollHeight: docH + scrollGap(cm) + d.barHeight, 3288 nativeBarWidth: d.nativeBarWidth, 3289 gutterWidth: gutterW 3290 } 3291 } 3292 3293 var NativeScrollbars = function(place, scroll, cm) { 3294 this.cm = cm 3295 var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") 3296 var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") 3297 place(vert); place(horiz) 3298 3299 on(vert, "scroll", function () { 3300 if (vert.clientHeight) { scroll(vert.scrollTop, "vertical") } 3301 }) 3302 on(horiz, "scroll", function () { 3303 if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal") } 3304 }) 3305 3306 this.checkedZeroWidth = false 3307 // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). 3308 if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" } 3309 }; 3310 3311 NativeScrollbars.prototype.update = function (measure) { 3312 var needsH = measure.scrollWidth > measure.clientWidth + 1 3313 var needsV = measure.scrollHeight > measure.clientHeight + 1 3314 var sWidth = measure.nativeBarWidth 3315 3316 if (needsV) { 3317 this.vert.style.display = "block" 3318 this.vert.style.bottom = needsH ? sWidth + "px" : "0" 3319 var totalHeight = measure.viewHeight - (needsH ? sWidth : 0) 3320 // A bug in IE8 can cause this value to be negative, so guard it. 3321 this.vert.firstChild.style.height = 3322 Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px" 3323 } else { 3324 this.vert.style.display = "" 3325 this.vert.firstChild.style.height = "0" 3326 } 3327 3328 if (needsH) { 3329 this.horiz.style.display = "block" 3330 this.horiz.style.right = needsV ? sWidth + "px" : "0" 3331 this.horiz.style.left = measure.barLeft + "px" 3332 var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) 3333 this.horiz.firstChild.style.width = 3334 (measure.scrollWidth - measure.clientWidth + totalWidth) + "px" 3335 } else { 3336 this.horiz.style.display = "" 3337 this.horiz.firstChild.style.width = "0" 3338 } 3339 3340 if (!this.checkedZeroWidth && measure.clientHeight > 0) { 3341 if (sWidth == 0) { this.zeroWidthHack() } 3342 this.checkedZeroWidth = true 3343 } 3344 3345 return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} 3346 }; 3347 3348 NativeScrollbars.prototype.setScrollLeft = function (pos) { 3349 if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos } 3350 if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz) } 3351 }; 3352 3353 NativeScrollbars.prototype.setScrollTop = function (pos) { 3354 if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos } 3355 if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) } 3356 }; 3357 3358 NativeScrollbars.prototype.zeroWidthHack = function () { 3359 var w = mac && !mac_geMountainLion ? "12px" : "18px" 3360 this.horiz.style.height = this.vert.style.width = w 3361 this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none" 3362 this.disableHoriz = new Delayed 3363 this.disableVert = new Delayed 3364 }; 3365 3366 NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay) { 3367 bar.style.pointerEvents = "auto" 3368 function maybeDisable() { 3369 // To find out whether the scrollbar is still visible, we 3370 // check whether the element under the pixel in the bottom 3371 // left corner of the scrollbar box is the scrollbar box 3372 // itself (when the bar is still visible) or its filler child 3373 // (when the bar is hidden). If it is still visible, we keep 3374 // it enabled, if it's hidden, we disable pointer events. 3375 var box = bar.getBoundingClientRect() 3376 var elt = document.elementFromPoint(box.left + 1, box.bottom - 1) 3377 if (elt != bar) { bar.style.pointerEvents = "none" } 3378 else { delay.set(1000, maybeDisable) } 3379 } 3380 delay.set(1000, maybeDisable) 3381 }; 3382 3383 NativeScrollbars.prototype.clear = function () { 3384 var parent = this.horiz.parentNode 3385 parent.removeChild(this.horiz) 3386 parent.removeChild(this.vert) 3387 }; 3388 3389 var NullScrollbars = function () {}; 3390 3391 NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; 3392 NullScrollbars.prototype.setScrollLeft = function () {}; 3393 NullScrollbars.prototype.setScrollTop = function () {}; 3394 NullScrollbars.prototype.clear = function () {}; 3395 3396 function updateScrollbars(cm, measure) { 3397 if (!measure) { measure = measureForScrollbars(cm) } 3398 var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight 3399 updateScrollbarsInner(cm, measure) 3400 for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { 3401 if (startWidth != cm.display.barWidth && cm.options.lineWrapping) 3402 { updateHeightsInViewport(cm) } 3403 updateScrollbarsInner(cm, measureForScrollbars(cm)) 3404 startWidth = cm.display.barWidth; startHeight = cm.display.barHeight 3405 } 3406 } 3407 3408 // Re-synchronize the fake scrollbars with the actual size of the 3409 // content. 3410 function updateScrollbarsInner(cm, measure) { 3411 var d = cm.display 3412 var sizes = d.scrollbars.update(measure) 3413 3414 d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px" 3415 d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px" 3416 d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent" 3417 3418 if (sizes.right && sizes.bottom) { 3419 d.scrollbarFiller.style.display = "block" 3420 d.scrollbarFiller.style.height = sizes.bottom + "px" 3421 d.scrollbarFiller.style.width = sizes.right + "px" 3422 } else { d.scrollbarFiller.style.display = "" } 3423 if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { 3424 d.gutterFiller.style.display = "block" 3425 d.gutterFiller.style.height = sizes.bottom + "px" 3426 d.gutterFiller.style.width = measure.gutterWidth + "px" 3427 } else { d.gutterFiller.style.display = "" } 3428 } 3429 3430 var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars} 3431 3432 function initScrollbars(cm) { 3433 if (cm.display.scrollbars) { 3434 cm.display.scrollbars.clear() 3435 if (cm.display.scrollbars.addClass) 3436 { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) } 3437 } 3438 3439 cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { 3440 cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller) 3441 // Prevent clicks in the scrollbars from killing focus 3442 on(node, "mousedown", function () { 3443 if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0) } 3444 }) 3445 node.setAttribute("cm-not-content", "true") 3446 }, function (pos, axis) { 3447 if (axis == "horizontal") { setScrollLeft(cm, pos) } 3448 else { setScrollTop(cm, pos) } 3449 }, cm) 3450 if (cm.display.scrollbars.addClass) 3451 { addClass(cm.display.wrapper, cm.display.scrollbars.addClass) } 3452 } 3453 3454 // SCROLLING THINGS INTO VIEW 3455 3456 // If an editor sits on the top or bottom of the window, partially 3457 // scrolled out of view, this ensures that the cursor is visible. 3458 function maybeScrollWindow(cm, coords) { 3459 if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } 3460 3461 var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null 3462 if (coords.top + box.top < 0) { doScroll = true } 3463 else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false } 3464 if (doScroll != null && !phantom) { 3465 var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (coords.left) + "px; width: 2px;")) 3466 cm.display.lineSpace.appendChild(scrollNode) 3467 scrollNode.scrollIntoView(doScroll) 3468 cm.display.lineSpace.removeChild(scrollNode) 3469 } 3470 } 3471 3472 // Scroll a given position into view (immediately), verifying that 3473 // it actually became visible (as line heights are accurately 3474 // measured, the position of something may 'drift' during drawing). 3475 function scrollPosIntoView(cm, pos, end, margin) { 3476 if (margin == null) { margin = 0 } 3477 var coords 3478 for (var limit = 0; limit < 5; limit++) { 3479 var changed = false 3480 coords = cursorCoords(cm, pos) 3481 var endCoords = !end || end == pos ? coords : cursorCoords(cm, end) 3482 var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left), 3483 Math.min(coords.top, endCoords.top) - margin, 3484 Math.max(coords.left, endCoords.left), 3485 Math.max(coords.bottom, endCoords.bottom) + margin) 3486 var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft 3487 if (scrollPos.scrollTop != null) { 3488 setScrollTop(cm, scrollPos.scrollTop) 3489 if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true } 3490 } 3491 if (scrollPos.scrollLeft != null) { 3492 setScrollLeft(cm, scrollPos.scrollLeft) 3493 if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true } 3494 } 3495 if (!changed) { break } 3496 } 3497 return coords 3498 } 3499 3500 // Scroll a given set of coordinates into view (immediately). 3501 function scrollIntoView(cm, x1, y1, x2, y2) { 3502 var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2) 3503 if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) } 3504 if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft) } 3505 } 3506 3507 // Calculate a new scroll position needed to scroll the given 3508 // rectangle into view. Returns an object with scrollTop and 3509 // scrollLeft properties. When these are undefined, the 3510 // vertical/horizontal position does not need to be adjusted. 3511 function calculateScrollPos(cm, x1, y1, x2, y2) { 3512 var display = cm.display, snapMargin = textHeight(cm.display) 3513 if (y1 < 0) { y1 = 0 } 3514 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop 3515 var screen = displayHeight(cm), result = {} 3516 if (y2 - y1 > screen) { y2 = y1 + screen } 3517 var docBottom = cm.doc.height + paddingVert(display) 3518 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin 3519 if (y1 < screentop) { 3520 result.scrollTop = atTop ? 0 : y1 3521 } else if (y2 > screentop + screen) { 3522 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen) 3523 if (newTop != screentop) { result.scrollTop = newTop } 3524 } 3525 3526 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft 3527 var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0) 3528 var tooWide = x2 - x1 > screenw 3529 if (tooWide) { x2 = x1 + screenw } 3530 if (x1 < 10) 3531 { result.scrollLeft = 0 } 3532 else if (x1 < screenleft) 3533 { result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)) } 3534 else if (x2 > screenw + screenleft - 3) 3535 { result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw } 3536 return result 3537 } 3538 3539 // Store a relative adjustment to the scroll position in the current 3540 // operation (to be applied when the operation finishes). 3541 function addToScrollPos(cm, left, top) { 3542 if (left != null || top != null) { resolveScrollToPos(cm) } 3543 if (left != null) 3544 { cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left } 3545 if (top != null) 3546 { cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top } 3547 } 3548 3549 // Make sure that at the end of the operation the current cursor is 3550 // shown. 3551 function ensureCursorVisible(cm) { 3552 resolveScrollToPos(cm) 3553 var cur = cm.getCursor(), from = cur, to = cur 3554 if (!cm.options.lineWrapping) { 3555 from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur 3556 to = Pos(cur.line, cur.ch + 1) 3557 } 3558 cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true} 3559 } 3560 3561 // When an operation has its scrollToPos property set, and another 3562 // scroll action is applied before the end of the operation, this 3563 // 'simulates' scrolling that position into view in a cheap way, so 3564 // that the effect of intermediate scroll commands is not ignored. 3565 function resolveScrollToPos(cm) { 3566 var range = cm.curOp.scrollToPos 3567 if (range) { 3568 cm.curOp.scrollToPos = null 3569 var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to) 3570 var sPos = calculateScrollPos(cm, Math.min(from.left, to.left), 3571 Math.min(from.top, to.top) - range.margin, 3572 Math.max(from.right, to.right), 3573 Math.max(from.bottom, to.bottom) + range.margin) 3574 cm.scrollTo(sPos.scrollLeft, sPos.scrollTop) 3575 } 3576 } 3577 3578 // Operations are used to wrap a series of changes to the editor 3579 // state in such a way that each change won't have to update the 3580 // cursor and display (which would be awkward, slow, and 3581 // error-prone). Instead, display updates are batched and then all 3582 // combined and executed at once. 3583 3584 var nextOpId = 0 3585 // Start a new operation. 3586 function startOperation(cm) { 3587 cm.curOp = { 3588 cm: cm, 3589 viewChanged: false, // Flag that indicates that lines might need to be redrawn 3590 startHeight: cm.doc.height, // Used to detect need to update scrollbar 3591 forceUpdate: false, // Used to force a redraw 3592 updateInput: null, // Whether to reset the input textarea 3593 typing: false, // Whether this reset should be careful to leave existing text (for compositing) 3594 changeObjs: null, // Accumulated changes, for firing change events 3595 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on 3596 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already 3597 selectionChanged: false, // Whether the selection needs to be redrawn 3598 updateMaxLine: false, // Set when the widest line needs to be determined anew 3599 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet 3600 scrollToPos: null, // Used to scroll to a specific position 3601 focus: false, 3602 id: ++nextOpId // Unique ID 3603 } 3604 pushOperation(cm.curOp) 3605 } 3606 3607 // Finish an operation, updating the display and signalling delayed events 3608 function endOperation(cm) { 3609 var op = cm.curOp 3610 finishOperation(op, function (group) { 3611 for (var i = 0; i < group.ops.length; i++) 3612 { group.ops[i].cm.curOp = null } 3613 endOperations(group) 3614 }) 3615 } 3616 3617 // The DOM updates done when an operation finishes are batched so 3618 // that the minimum number of relayouts are required. 3619 function endOperations(group) { 3620 var ops = group.ops 3621 for (var i = 0; i < ops.length; i++) // Read DOM 3622 { endOperation_R1(ops[i]) } 3623 for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) 3624 { endOperation_W1(ops[i$1]) } 3625 for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM 3626 { endOperation_R2(ops[i$2]) } 3627 for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) 3628 { endOperation_W2(ops[i$3]) } 3629 for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM 3630 { endOperation_finish(ops[i$4]) } 3631 } 3632 3633 function endOperation_R1(op) { 3634 var cm = op.cm, display = cm.display 3635 maybeClipScrollbars(cm) 3636 if (op.updateMaxLine) { findMaxLine(cm) } 3637 3638 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || 3639 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || 3640 op.scrollToPos.to.line >= display.viewTo) || 3641 display.maxLineChanged && cm.options.lineWrapping 3642 op.update = op.mustUpdate && 3643 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate) 3644 } 3645 3646 function endOperation_W1(op) { 3647 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update) 3648 } 3649 3650 function endOperation_R2(op) { 3651 var cm = op.cm, display = cm.display 3652 if (op.updatedDisplay) { updateHeightsInViewport(cm) } 3653 3654 op.barMeasure = measureForScrollbars(cm) 3655 3656 // If the max line changed since it was last measured, measure it, 3657 // and ensure the document's width matches it. 3658 // updateDisplay_W2 will use these properties to do the actual resizing 3659 if (display.maxLineChanged && !cm.options.lineWrapping) { 3660 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3 3661 cm.display.sizerWidth = op.adjustWidthTo 3662 op.barMeasure.scrollWidth = 3663 Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth) 3664 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)) 3665 } 3666 3667 if (op.updatedDisplay || op.selectionChanged) 3668 { op.preparedSelection = display.input.prepareSelection(op.focus) } 3669 } 3670 3671 function endOperation_W2(op) { 3672 var cm = op.cm 3673 3674 if (op.adjustWidthTo != null) { 3675 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px" 3676 if (op.maxScrollLeft < cm.doc.scrollLeft) 3677 { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) } 3678 cm.display.maxLineChanged = false 3679 } 3680 3681 var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()) 3682 if (op.preparedSelection) 3683 { cm.display.input.showSelection(op.preparedSelection, takeFocus) } 3684 if (op.updatedDisplay || op.startHeight != cm.doc.height) 3685 { updateScrollbars(cm, op.barMeasure) } 3686 if (op.updatedDisplay) 3687 { setDocumentHeight(cm, op.barMeasure) } 3688 3689 if (op.selectionChanged) { restartBlink(cm) } 3690 3691 if (cm.state.focused && op.updateInput) 3692 { cm.display.input.reset(op.typing) } 3693 if (takeFocus) { ensureFocus(op.cm) } 3694 } 3695 3696 function endOperation_finish(op) { 3697 var cm = op.cm, display = cm.display, doc = cm.doc 3698 3699 if (op.updatedDisplay) { postUpdateDisplay(cm, op.update) } 3700 3701 // Abort mouse wheel delta measurement, when scrolling explicitly 3702 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) 3703 { display.wheelStartX = display.wheelStartY = null } 3704 3705 // Propagate the scroll position to the actual DOM scroller 3706 if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) { 3707 doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)) 3708 display.scrollbars.setScrollTop(doc.scrollTop) 3709 display.scroller.scrollTop = doc.scrollTop 3710 } 3711 if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) { 3712 doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft)) 3713 display.scrollbars.setScrollLeft(doc.scrollLeft) 3714 display.scroller.scrollLeft = doc.scrollLeft 3715 alignHorizontally(cm) 3716 } 3717 // If we need to scroll a specific position into view, do so. 3718 if (op.scrollToPos) { 3719 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), 3720 clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin) 3721 if (op.scrollToPos.isCursor && cm.state.focused) { maybeScrollWindow(cm, coords) } 3722 } 3723 3724 // Fire events for markers that are hidden/unidden by editing or 3725 // undoing 3726 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers 3727 if (hidden) { for (var i = 0; i < hidden.length; ++i) 3728 { if (!hidden[i].lines.length) { signal(hidden[i], "hide") } } } 3729 if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) 3730 { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide") } } } 3731 3732 if (display.wrapper.offsetHeight) 3733 { doc.scrollTop = cm.display.scroller.scrollTop } 3734 3735 // Fire change events, and delayed event handlers 3736 if (op.changeObjs) 3737 { signal(cm, "changes", cm, op.changeObjs) } 3738 if (op.update) 3739 { op.update.finish() } 3740 } 3741 3742 // Run the given function in an operation 3743 function runInOp(cm, f) { 3744 if (cm.curOp) { return f() } 3745 startOperation(cm) 3746 try { return f() } 3747 finally { endOperation(cm) } 3748 } 3749 // Wraps a function in an operation. Returns the wrapped function. 3750 function operation(cm, f) { 3751 return function() { 3752 if (cm.curOp) { return f.apply(cm, arguments) } 3753 startOperation(cm) 3754 try { return f.apply(cm, arguments) } 3755 finally { endOperation(cm) } 3756 } 3757 } 3758 // Used to add methods to editor and doc instances, wrapping them in 3759 // operations. 3760 function methodOp(f) { 3761 return function() { 3762 if (this.curOp) { return f.apply(this, arguments) } 3763 startOperation(this) 3764 try { return f.apply(this, arguments) } 3765 finally { endOperation(this) } 3766 } 3767 } 3768 function docMethodOp(f) { 3769 return function() { 3770 var cm = this.cm 3771 if (!cm || cm.curOp) { return f.apply(this, arguments) } 3772 startOperation(cm) 3773 try { return f.apply(this, arguments) } 3774 finally { endOperation(cm) } 3775 } 3776 } 3777 3778 // Updates the display.view data structure for a given change to the 3779 // document. From and to are in pre-change coordinates. Lendiff is 3780 // the amount of lines added or subtracted by the change. This is 3781 // used for changes that span multiple lines, or change the way 3782 // lines are divided into visual lines. regLineChange (below) 3783 // registers single-line changes. 3784 function regChange(cm, from, to, lendiff) { 3785 if (from == null) { from = cm.doc.first } 3786 if (to == null) { to = cm.doc.first + cm.doc.size } 3787 if (!lendiff) { lendiff = 0 } 3788 3789 var display = cm.display 3790 if (lendiff && to < display.viewTo && 3791 (display.updateLineNumbers == null || display.updateLineNumbers > from)) 3792 { display.updateLineNumbers = from } 3793 3794 cm.curOp.viewChanged = true 3795 3796 if (from >= display.viewTo) { // Change after 3797 if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) 3798 { resetView(cm) } 3799 } else if (to <= display.viewFrom) { // Change before 3800 if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { 3801 resetView(cm) 3802 } else { 3803 display.viewFrom += lendiff 3804 display.viewTo += lendiff 3805 } 3806 } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap 3807 resetView(cm) 3808 } else if (from <= display.viewFrom) { // Top overlap 3809 var cut = viewCuttingPoint(cm, to, to + lendiff, 1) 3810 if (cut) { 3811 display.view = display.view.slice(cut.index) 3812 display.viewFrom = cut.lineN 3813 display.viewTo += lendiff 3814 } else { 3815 resetView(cm) 3816 } 3817 } else if (to >= display.viewTo) { // Bottom overlap 3818 var cut$1 = viewCuttingPoint(cm, from, from, -1) 3819 if (cut$1) { 3820 display.view = display.view.slice(0, cut$1.index) 3821 display.viewTo = cut$1.lineN 3822 } else { 3823 resetView(cm) 3824 } 3825 } else { // Gap in the middle 3826 var cutTop = viewCuttingPoint(cm, from, from, -1) 3827 var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1) 3828 if (cutTop && cutBot) { 3829 display.view = display.view.slice(0, cutTop.index) 3830 .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) 3831 .concat(display.view.slice(cutBot.index)) 3832 display.viewTo += lendiff 3833 } else { 3834 resetView(cm) 3835 } 3836 } 3837 3838 var ext = display.externalMeasured 3839 if (ext) { 3840 if (to < ext.lineN) 3841 { ext.lineN += lendiff } 3842 else if (from < ext.lineN + ext.size) 3843 { display.externalMeasured = null } 3844 } 3845 } 3846 3847 // Register a change to a single line. Type must be one of "text", 3848 // "gutter", "class", "widget" 3849 function regLineChange(cm, line, type) { 3850 cm.curOp.viewChanged = true 3851 var display = cm.display, ext = cm.display.externalMeasured 3852 if (ext && line >= ext.lineN && line < ext.lineN + ext.size) 3853 { display.externalMeasured = null } 3854 3855 if (line < display.viewFrom || line >= display.viewTo) { return } 3856 var lineView = display.view[findViewIndex(cm, line)] 3857 if (lineView.node == null) { return } 3858 var arr = lineView.changes || (lineView.changes = []) 3859 if (indexOf(arr, type) == -1) { arr.push(type) } 3860 } 3861 3862 // Clear the view. 3863 function resetView(cm) { 3864 cm.display.viewFrom = cm.display.viewTo = cm.doc.first 3865 cm.display.view = [] 3866 cm.display.viewOffset = 0 3867 } 3868 3869 function viewCuttingPoint(cm, oldN, newN, dir) { 3870 var index = findViewIndex(cm, oldN), diff, view = cm.display.view 3871 if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) 3872 { return {index: index, lineN: newN} } 3873 var n = cm.display.viewFrom 3874 for (var i = 0; i < index; i++) 3875 { n += view[i].size } 3876 if (n != oldN) { 3877 if (dir > 0) { 3878 if (index == view.length - 1) { return null } 3879 diff = (n + view[index].size) - oldN 3880 index++ 3881 } else { 3882 diff = n - oldN 3883 } 3884 oldN += diff; newN += diff 3885 } 3886 while (visualLineNo(cm.doc, newN) != newN) { 3887 if (index == (dir < 0 ? 0 : view.length - 1)) { return null } 3888 newN += dir * view[index - (dir < 0 ? 1 : 0)].size 3889 index += dir 3890 } 3891 return {index: index, lineN: newN} 3892 } 3893 3894 // Force the view to cover a given range, adding empty view element 3895 // or clipping off existing ones as needed. 3896 function adjustView(cm, from, to) { 3897 var display = cm.display, view = display.view 3898 if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { 3899 display.view = buildViewArray(cm, from, to) 3900 display.viewFrom = from 3901 } else { 3902 if (display.viewFrom > from) 3903 { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) } 3904 else if (display.viewFrom < from) 3905 { display.view = display.view.slice(findViewIndex(cm, from)) } 3906 display.viewFrom = from 3907 if (display.viewTo < to) 3908 { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) } 3909 else if (display.viewTo > to) 3910 { display.view = display.view.slice(0, findViewIndex(cm, to)) } 3911 } 3912 display.viewTo = to 3913 } 3914 3915 // Count the number of lines in the view whose DOM representation is 3916 // out of date (or nonexistent). 3917 function countDirtyView(cm) { 3918 var view = cm.display.view, dirty = 0 3919 for (var i = 0; i < view.length; i++) { 3920 var lineView = view[i] 3921 if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty } 3922 } 3923 return dirty 3924 } 3925 3926 // HIGHLIGHT WORKER 3927 3928 function startWorker(cm, time) { 3929 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) 3930 { cm.state.highlight.set(time, bind(highlightWorker, cm)) } 3931 } 3932 3933 function highlightWorker(cm) { 3934 var doc = cm.doc 3935 if (doc.frontier < doc.first) { doc.frontier = doc.first } 3936 if (doc.frontier >= cm.display.viewTo) { return } 3937 var end = +new Date + cm.options.workTime 3938 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)) 3939 var changedLines = [] 3940 3941 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { 3942 if (doc.frontier >= cm.display.viewFrom) { // Visible 3943 var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength 3944 var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true) 3945 line.styles = highlighted.styles 3946 var oldCls = line.styleClasses, newCls = highlighted.classes 3947 if (newCls) { line.styleClasses = newCls } 3948 else if (oldCls) { line.styleClasses = null } 3949 var ischange = !oldStyles || oldStyles.length != line.styles.length || 3950 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass) 3951 for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i] } 3952 if (ischange) { changedLines.push(doc.frontier) } 3953 line.stateAfter = tooLong ? state : copyState(doc.mode, state) 3954 } else { 3955 if (line.text.length <= cm.options.maxHighlightLength) 3956 { processLine(cm, line.text, state) } 3957 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null 3958 } 3959 ++doc.frontier 3960 if (+new Date > end) { 3961 startWorker(cm, cm.options.workDelay) 3962 return true 3963 } 3964 }) 3965 if (changedLines.length) { runInOp(cm, function () { 3966 for (var i = 0; i < changedLines.length; i++) 3967 { regLineChange(cm, changedLines[i], "text") } 3968 }) } 3969 } 3970 3971 // DISPLAY DRAWING 3972 3973 var DisplayUpdate = function(cm, viewport, force) { 3974 var display = cm.display 3975 3976 this.viewport = viewport 3977 // Store some values that we'll need later (but don't want to force a relayout for) 3978 this.visible = visibleLines(display, cm.doc, viewport) 3979 this.editorIsHidden = !display.wrapper.offsetWidth 3980 this.wrapperHeight = display.wrapper.clientHeight 3981 this.wrapperWidth = display.wrapper.clientWidth 3982 this.oldDisplayWidth = displayWidth(cm) 3983 this.force = force 3984 this.dims = getDimensions(cm) 3985 this.events = [] 3986 }; 3987 3988 DisplayUpdate.prototype.signal = function (emitter, type) { 3989 if (hasHandler(emitter, type)) 3990 { this.events.push(arguments) } 3991 }; 3992 DisplayUpdate.prototype.finish = function () { 3993 var this$1 = this; 3994 3995 for (var i = 0; i < this.events.length; i++) 3996 { signal.apply(null, this$1.events[i]) } 3997 }; 3998 3999 function maybeClipScrollbars(cm) { 4000 var display = cm.display 4001 if (!display.scrollbarsClipped && display.scroller.offsetWidth) { 4002 display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth 4003 display.heightForcer.style.height = scrollGap(cm) + "px" 4004 display.sizer.style.marginBottom = -display.nativeBarWidth + "px" 4005 display.sizer.style.borderRightWidth = scrollGap(cm) + "px" 4006 display.scrollbarsClipped = true 4007 } 4008 } 4009 4010 // Does the actual updating of the line display. Bails out 4011 // (returning false) when there is nothing to be done and forced is 4012 // false. 4013 function updateDisplayIfNeeded(cm, update) { 4014 var display = cm.display, doc = cm.doc 4015 4016 if (update.editorIsHidden) { 4017 resetView(cm) 4018 return false 4019 } 4020 4021 // Bail out if the visible area is already rendered and nothing changed. 4022 if (!update.force && 4023 update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && 4024 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && 4025 display.renderedView == display.view && countDirtyView(cm) == 0) 4026 { return false } 4027 4028 if (maybeUpdateLineNumberWidth(cm)) { 4029 resetView(cm) 4030 update.dims = getDimensions(cm) 4031 } 4032 4033 // Compute a suitable new viewport (from & to) 4034 var end = doc.first + doc.size 4035 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first) 4036 var to = Math.min(end, update.visible.to + cm.options.viewportMargin) 4037 if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom) } 4038 if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo) } 4039 if (sawCollapsedSpans) { 4040 from = visualLineNo(cm.doc, from) 4041 to = visualLineEndNo(cm.doc, to) 4042 } 4043 4044 var different = from != display.viewFrom || to != display.viewTo || 4045 display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth 4046 adjustView(cm, from, to) 4047 4048 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)) 4049 // Position the mover div to align with the current scroll position 4050 cm.display.mover.style.top = display.viewOffset + "px" 4051 4052 var toUpdate = countDirtyView(cm) 4053 if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && 4054 (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) 4055 { return false } 4056 4057 // For big changes, we hide the enclosing element during the 4058 // update, since that speeds up the operations on most browsers. 4059 var focused = activeElt() 4060 if (toUpdate > 4) { display.lineDiv.style.display = "none" } 4061 patchDisplay(cm, display.updateLineNumbers, update.dims) 4062 if (toUpdate > 4) { display.lineDiv.style.display = "" } 4063 display.renderedView = display.view 4064 // There might have been a widget with a focused element that got 4065 // hidden or updated, if so re-focus it. 4066 if (focused && activeElt() != focused && focused.offsetHeight) { focused.focus() } 4067 4068 // Prevent selection and cursors from interfering with the scroll 4069 // width and height. 4070 removeChildren(display.cursorDiv) 4071 removeChildren(display.selectionDiv) 4072 display.gutters.style.height = display.sizer.style.minHeight = 0 4073 4074 if (different) { 4075 display.lastWrapHeight = update.wrapperHeight 4076 display.lastWrapWidth = update.wrapperWidth 4077 startWorker(cm, 400) 4078 } 4079 4080 display.updateLineNumbers = null 4081 4082 return true 4083 } 4084 4085 function postUpdateDisplay(cm, update) { 4086 var viewport = update.viewport 4087 4088 for (var first = true;; first = false) { 4089 if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { 4090 // Clip forced viewport to actual scrollable area. 4091 if (viewport && viewport.top != null) 4092 { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} } 4093 // Updated line heights might result in the drawn area not 4094 // actually covering the viewport. Keep looping until it does. 4095 update.visible = visibleLines(cm.display, cm.doc, viewport) 4096 if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) 4097 { break } 4098 } 4099 if (!updateDisplayIfNeeded(cm, update)) { break } 4100 updateHeightsInViewport(cm) 4101 var barMeasure = measureForScrollbars(cm) 4102 updateSelection(cm) 4103 updateScrollbars(cm, barMeasure) 4104 setDocumentHeight(cm, barMeasure) 4105 } 4106 4107 update.signal(cm, "update", cm) 4108 if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { 4109 update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo) 4110 cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo 4111 } 4112 } 4113 4114 function updateDisplaySimple(cm, viewport) { 4115 var update = new DisplayUpdate(cm, viewport) 4116 if (updateDisplayIfNeeded(cm, update)) { 4117 updateHeightsInViewport(cm) 4118 postUpdateDisplay(cm, update) 4119 var barMeasure = measureForScrollbars(cm) 4120 updateSelection(cm) 4121 updateScrollbars(cm, barMeasure) 4122 setDocumentHeight(cm, barMeasure) 4123 update.finish() 4124 } 4125 } 4126 4127 // Sync the actual display DOM structure with display.view, removing 4128 // nodes for lines that are no longer in view, and creating the ones 4129 // that are not there yet, and updating the ones that are out of 4130 // date. 4131 function patchDisplay(cm, updateNumbersFrom, dims) { 4132 var display = cm.display, lineNumbers = cm.options.lineNumbers 4133 var container = display.lineDiv, cur = container.firstChild 4134 4135 function rm(node) { 4136 var next = node.nextSibling 4137 // Works around a throw-scroll bug in OS X Webkit 4138 if (webkit && mac && cm.display.currentWheelTarget == node) 4139 { node.style.display = "none" } 4140 else 4141 { node.parentNode.removeChild(node) } 4142 return next 4143 } 4144 4145 var view = display.view, lineN = display.viewFrom 4146 // Loop over the elements in the view, syncing cur (the DOM nodes 4147 // in display.lineDiv) with the view as we go. 4148 for (var i = 0; i < view.length; i++) { 4149 var lineView = view[i] 4150 if (lineView.hidden) { 4151 } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet 4152 var node = buildLineElement(cm, lineView, lineN, dims) 4153 container.insertBefore(node, cur) 4154 } else { // Already drawn 4155 while (cur != lineView.node) { cur = rm(cur) } 4156 var updateNumber = lineNumbers && updateNumbersFrom != null && 4157 updateNumbersFrom <= lineN && lineView.lineNumber 4158 if (lineView.changes) { 4159 if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false } 4160 updateLineForChanges(cm, lineView, lineN, dims) 4161 } 4162 if (updateNumber) { 4163 removeChildren(lineView.lineNumber) 4164 lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))) 4165 } 4166 cur = lineView.node.nextSibling 4167 } 4168 lineN += lineView.size 4169 } 4170 while (cur) { cur = rm(cur) } 4171 } 4172 4173 function updateGutterSpace(cm) { 4174 var width = cm.display.gutters.offsetWidth 4175 cm.display.sizer.style.marginLeft = width + "px" 4176 } 4177 4178 function setDocumentHeight(cm, measure) { 4179 cm.display.sizer.style.minHeight = measure.docHeight + "px" 4180 cm.display.heightForcer.style.top = measure.docHeight + "px" 4181 cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px" 4182 } 4183 4184 // Rebuild the gutter elements, ensure the margin to the left of the 4185 // code matches their width. 4186 function updateGutters(cm) { 4187 var gutters = cm.display.gutters, specs = cm.options.gutters 4188 removeChildren(gutters) 4189 var i = 0 4190 for (; i < specs.length; ++i) { 4191 var gutterClass = specs[i] 4192 var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)) 4193 if (gutterClass == "CodeMirror-linenumbers") { 4194 cm.display.lineGutter = gElt 4195 gElt.style.width = (cm.display.lineNumWidth || 1) + "px" 4196 } 4197 } 4198 gutters.style.display = i ? "" : "none" 4199 updateGutterSpace(cm) 4200 } 4201 4202 // Make sure the gutters options contains the element 4203 // "CodeMirror-linenumbers" when the lineNumbers option is true. 4204 function setGuttersForLineNumbers(options) { 4205 var found = indexOf(options.gutters, "CodeMirror-linenumbers") 4206 if (found == -1 && options.lineNumbers) { 4207 options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]) 4208 } else if (found > -1 && !options.lineNumbers) { 4209 options.gutters = options.gutters.slice(0) 4210 options.gutters.splice(found, 1) 4211 } 4212 } 4213 4214 // Selection objects are immutable. A new one is created every time 4215 // the selection changes. A selection is one or more non-overlapping 4216 // (and non-touching) ranges, sorted, and an integer that indicates 4217 // which one is the primary selection (the one that's scrolled into 4218 // view, that getCursor returns, etc). 4219 function Selection(ranges, primIndex) { 4220 this.ranges = ranges 4221 this.primIndex = primIndex 4222 } 4223 4224 Selection.prototype = { 4225 primary: function() { return this.ranges[this.primIndex] }, 4226 equals: function(other) { 4227 var this$1 = this; 4228 4229 if (other == this) { return true } 4230 if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } 4231 for (var i = 0; i < this.ranges.length; i++) { 4232 var here = this$1.ranges[i], there = other.ranges[i] 4233 if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) { return false } 4234 } 4235 return true 4236 }, 4237 deepCopy: function() { 4238 var this$1 = this; 4239 4240 var out = [] 4241 for (var i = 0; i < this.ranges.length; i++) 4242 { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)) } 4243 return new Selection(out, this.primIndex) 4244 }, 4245 somethingSelected: function() { 4246 var this$1 = this; 4247 4248 for (var i = 0; i < this.ranges.length; i++) 4249 { if (!this$1.ranges[i].empty()) { return true } } 4250 return false 4251 }, 4252 contains: function(pos, end) { 4253 var this$1 = this; 4254 4255 if (!end) { end = pos } 4256 for (var i = 0; i < this.ranges.length; i++) { 4257 var range = this$1.ranges[i] 4258 if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) 4259 { return i } 4260 } 4261 return -1 4262 } 4263 } 4264 4265 function Range(anchor, head) { 4266 this.anchor = anchor; this.head = head 4267 } 4268 4269 Range.prototype = { 4270 from: function() { return minPos(this.anchor, this.head) }, 4271 to: function() { return maxPos(this.anchor, this.head) }, 4272 empty: function() { 4273 return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch 4274 } 4275 } 4276 4277 // Take an unsorted, potentially overlapping set of ranges, and 4278 // build a selection out of it. 'Consumes' ranges array (modifying 4279 // it). 4280 function normalizeSelection(ranges, primIndex) { 4281 var prim = ranges[primIndex] 4282 ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }) 4283 primIndex = indexOf(ranges, prim) 4284 for (var i = 1; i < ranges.length; i++) { 4285 var cur = ranges[i], prev = ranges[i - 1] 4286 if (cmp(prev.to(), cur.from()) >= 0) { 4287 var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()) 4288 var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head 4289 if (i <= primIndex) { --primIndex } 4290 ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)) 4291 } 4292 } 4293 return new Selection(ranges, primIndex) 4294 } 4295 4296 function simpleSelection(anchor, head) { 4297 return new Selection([new Range(anchor, head || anchor)], 0) 4298 } 4299 4300 // Compute the position of the end of a change (its 'to' property 4301 // refers to the pre-change end). 4302 function changeEnd(change) { 4303 if (!change.text) { return change.to } 4304 return Pos(change.from.line + change.text.length - 1, 4305 lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) 4306 } 4307 4308 // Adjust a position to refer to the post-change position of the 4309 // same text, or the end of the change if the change covers it. 4310 function adjustForChange(pos, change) { 4311 if (cmp(pos, change.from) < 0) { return pos } 4312 if (cmp(pos, change.to) <= 0) { return changeEnd(change) } 4313 4314 var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch 4315 if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch } 4316 return Pos(line, ch) 4317 } 4318 4319 function computeSelAfterChange(doc, change) { 4320 var out = [] 4321 for (var i = 0; i < doc.sel.ranges.length; i++) { 4322 var range = doc.sel.ranges[i] 4323 out.push(new Range(adjustForChange(range.anchor, change), 4324 adjustForChange(range.head, change))) 4325 } 4326 return normalizeSelection(out, doc.sel.primIndex) 4327 } 4328 4329 function offsetPos(pos, old, nw) { 4330 if (pos.line == old.line) 4331 { return Pos(nw.line, pos.ch - old.ch + nw.ch) } 4332 else 4333 { return Pos(nw.line + (pos.line - old.line), pos.ch) } 4334 } 4335 4336 // Used by replaceSelections to allow moving the selection to the 4337 // start or around the replaced test. Hint may be "start" or "around". 4338 function computeReplacedSel(doc, changes, hint) { 4339 var out = [] 4340 var oldPrev = Pos(doc.first, 0), newPrev = oldPrev 4341 for (var i = 0; i < changes.length; i++) { 4342 var change = changes[i] 4343 var from = offsetPos(change.from, oldPrev, newPrev) 4344 var to = offsetPos(changeEnd(change), oldPrev, newPrev) 4345 oldPrev = change.to 4346 newPrev = to 4347 if (hint == "around") { 4348 var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0 4349 out[i] = new Range(inv ? to : from, inv ? from : to) 4350 } else { 4351 out[i] = new Range(from, from) 4352 } 4353 } 4354 return new Selection(out, doc.sel.primIndex) 4355 } 4356 4357 // Used to get the editor into a consistent state again when options change. 4358 4359 function loadMode(cm) { 4360 cm.doc.mode = getMode(cm.options, cm.doc.modeOption) 4361 resetModeState(cm) 4362 } 4363 4364 function resetModeState(cm) { 4365 cm.doc.iter(function (line) { 4366 if (line.stateAfter) { line.stateAfter = null } 4367 if (line.styles) { line.styles = null } 4368 }) 4369 cm.doc.frontier = cm.doc.first 4370 startWorker(cm, 100) 4371 cm.state.modeGen++ 4372 if (cm.curOp) { regChange(cm) } 4373 } 4374 4375 // DOCUMENT DATA STRUCTURE 4376 4377 // By default, updates that start and end at the beginning of a line 4378 // are treated specially, in order to make the association of line 4379 // widgets and marker elements with the text behave more intuitive. 4380 function isWholeLineUpdate(doc, change) { 4381 return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && 4382 (!doc.cm || doc.cm.options.wholeLineUpdateBefore) 4383 } 4384 4385 // Perform a change on the document data structure. 4386 function updateDoc(doc, change, markedSpans, estimateHeight) { 4387 function spansFor(n) {return markedSpans ? markedSpans[n] : null} 4388 function update(line, text, spans) { 4389 updateLine(line, text, spans, estimateHeight) 4390 signalLater(line, "change", line, change) 4391 } 4392 function linesFor(start, end) { 4393 var result = [] 4394 for (var i = start; i < end; ++i) 4395 { result.push(new Line(text[i], spansFor(i), estimateHeight)) } 4396 return result 4397 } 4398 4399 var from = change.from, to = change.to, text = change.text 4400 var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line) 4401 var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line 4402 4403 // Adjust the line structure 4404 if (change.full) { 4405 doc.insert(0, linesFor(0, text.length)) 4406 doc.remove(text.length, doc.size - text.length) 4407 } else if (isWholeLineUpdate(doc, change)) { 4408 // This is a whole-line replace. Treated specially to make 4409 // sure line objects move the way they are supposed to. 4410 var added = linesFor(0, text.length - 1) 4411 update(lastLine, lastLine.text, lastSpans) 4412 if (nlines) { doc.remove(from.line, nlines) } 4413 if (added.length) { doc.insert(from.line, added) } 4414 } else if (firstLine == lastLine) { 4415 if (text.length == 1) { 4416 update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans) 4417 } else { 4418 var added$1 = linesFor(1, text.length - 1) 4419 added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)) 4420 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) 4421 doc.insert(from.line + 1, added$1) 4422 } 4423 } else if (text.length == 1) { 4424 update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)) 4425 doc.remove(from.line + 1, nlines) 4426 } else { 4427 update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) 4428 update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans) 4429 var added$2 = linesFor(1, text.length - 1) 4430 if (nlines > 1) { doc.remove(from.line + 1, nlines - 1) } 4431 doc.insert(from.line + 1, added$2) 4432 } 4433 4434 signalLater(doc, "change", doc, change) 4435 } 4436 4437 // Call f for all linked documents. 4438 function linkedDocs(doc, f, sharedHistOnly) { 4439 function propagate(doc, skip, sharedHist) { 4440 if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { 4441 var rel = doc.linked[i] 4442 if (rel.doc == skip) { continue } 4443 var shared = sharedHist && rel.sharedHist 4444 if (sharedHistOnly && !shared) { continue } 4445 f(rel.doc, shared) 4446 propagate(rel.doc, doc, shared) 4447 } } 4448 } 4449 propagate(doc, null, true) 4450 } 4451 4452 // Attach a document to an editor. 4453 function attachDoc(cm, doc) { 4454 if (doc.cm) { throw new Error("This document is already in use.") } 4455 cm.doc = doc 4456 doc.cm = cm 4457 estimateLineHeights(cm) 4458 loadMode(cm) 4459 if (!cm.options.lineWrapping) { findMaxLine(cm) } 4460 cm.options.mode = doc.modeOption 4461 regChange(cm) 4462 } 4463 4464 function History(startGen) { 4465 // Arrays of change events and selections. Doing something adds an 4466 // event to done and clears undo. Undoing moves events from done 4467 // to undone, redoing moves them in the other direction. 4468 this.done = []; this.undone = [] 4469 this.undoDepth = Infinity 4470 // Used to track when changes can be merged into a single undo 4471 // event 4472 this.lastModTime = this.lastSelTime = 0 4473 this.lastOp = this.lastSelOp = null 4474 this.lastOrigin = this.lastSelOrigin = null 4475 // Used by the isClean() method 4476 this.generation = this.maxGeneration = startGen || 1 4477 } 4478 4479 // Create a history change event from an updateDoc-style change 4480 // object. 4481 function historyChangeFromChange(doc, change) { 4482 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)} 4483 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1) 4484 linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true) 4485 return histChange 4486 } 4487 4488 // Pop all selection events off the end of a history array. Stop at 4489 // a change event. 4490 function clearSelectionEvents(array) { 4491 while (array.length) { 4492 var last = lst(array) 4493 if (last.ranges) { array.pop() } 4494 else { break } 4495 } 4496 } 4497 4498 // Find the top change event in the history. Pop off selection 4499 // events that are in the way. 4500 function lastChangeEvent(hist, force) { 4501 if (force) { 4502 clearSelectionEvents(hist.done) 4503 return lst(hist.done) 4504 } else if (hist.done.length && !lst(hist.done).ranges) { 4505 return lst(hist.done) 4506 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { 4507 hist.done.pop() 4508 return lst(hist.done) 4509 } 4510 } 4511 4512 // Register a change in the history. Merges changes that are within 4513 // a single operation, or are close together with an origin that 4514 // allows merging (starting with "+") into a single event. 4515 function addChangeToHistory(doc, change, selAfter, opId) { 4516 var hist = doc.history 4517 hist.undone.length = 0 4518 var time = +new Date, cur 4519 var last 4520 4521 if ((hist.lastOp == opId || 4522 hist.lastOrigin == change.origin && change.origin && 4523 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || 4524 change.origin.charAt(0) == "*")) && 4525 (cur = lastChangeEvent(hist, hist.lastOp == opId))) { 4526 // Merge this change into the last event 4527 last = lst(cur.changes) 4528 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { 4529 // Optimized case for simple insertion -- don't want to add 4530 // new changesets for every character typed 4531 last.to = changeEnd(change) 4532 } else { 4533 // Add new sub-event 4534 cur.changes.push(historyChangeFromChange(doc, change)) 4535 } 4536 } else { 4537 // Can not be merged, start a new event. 4538 var before = lst(hist.done) 4539 if (!before || !before.ranges) 4540 { pushSelectionToHistory(doc.sel, hist.done) } 4541 cur = {changes: [historyChangeFromChange(doc, change)], 4542 generation: hist.generation} 4543 hist.done.push(cur) 4544 while (hist.done.length > hist.undoDepth) { 4545 hist.done.shift() 4546 if (!hist.done[0].ranges) { hist.done.shift() } 4547 } 4548 } 4549 hist.done.push(selAfter) 4550 hist.generation = ++hist.maxGeneration 4551 hist.lastModTime = hist.lastSelTime = time 4552 hist.lastOp = hist.lastSelOp = opId 4553 hist.lastOrigin = hist.lastSelOrigin = change.origin 4554 4555 if (!last) { signal(doc, "historyAdded") } 4556 } 4557 4558 function selectionEventCanBeMerged(doc, origin, prev, sel) { 4559 var ch = origin.charAt(0) 4560 return ch == "*" || 4561 ch == "+" && 4562 prev.ranges.length == sel.ranges.length && 4563 prev.somethingSelected() == sel.somethingSelected() && 4564 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) 4565 } 4566 4567 // Called whenever the selection changes, sets the new selection as 4568 // the pending selection in the history, and pushes the old pending 4569 // selection into the 'done' array when it was significantly 4570 // different (in number of selected ranges, emptiness, or time). 4571 function addSelectionToHistory(doc, sel, opId, options) { 4572 var hist = doc.history, origin = options && options.origin 4573 4574 // A new event is started when the previous origin does not match 4575 // the current, or the origins don't allow matching. Origins 4576 // starting with * are always merged, those starting with + are 4577 // merged when similar and close together in time. 4578 if (opId == hist.lastSelOp || 4579 (origin && hist.lastSelOrigin == origin && 4580 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || 4581 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) 4582 { hist.done[hist.done.length - 1] = sel } 4583 else 4584 { pushSelectionToHistory(sel, hist.done) } 4585 4586 hist.lastSelTime = +new Date 4587 hist.lastSelOrigin = origin 4588 hist.lastSelOp = opId 4589 if (options && options.clearRedo !== false) 4590 { clearSelectionEvents(hist.undone) } 4591 } 4592 4593 function pushSelectionToHistory(sel, dest) { 4594 var top = lst(dest) 4595 if (!(top && top.ranges && top.equals(sel))) 4596 { dest.push(sel) } 4597 } 4598 4599 // Used to store marked span information in the history. 4600 function attachLocalSpans(doc, change, from, to) { 4601 var existing = change["spans_" + doc.id], n = 0 4602 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { 4603 if (line.markedSpans) 4604 { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans } 4605 ++n 4606 }) 4607 } 4608 4609 // When un/re-doing restores text containing marked spans, those 4610 // that have been explicitly cleared should not be restored. 4611 function removeClearedSpans(spans) { 4612 if (!spans) { return null } 4613 var out 4614 for (var i = 0; i < spans.length; ++i) { 4615 if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i) } } 4616 else if (out) { out.push(spans[i]) } 4617 } 4618 return !out ? spans : out.length ? out : null 4619 } 4620 4621 // Retrieve and filter the old marked spans stored in a change event. 4622 function getOldSpans(doc, change) { 4623 var found = change["spans_" + doc.id] 4624 if (!found) { return null } 4625 var nw = [] 4626 for (var i = 0; i < change.text.length; ++i) 4627 { nw.push(removeClearedSpans(found[i])) } 4628 return nw 4629 } 4630 4631 // Used for un/re-doing changes from the history. Combines the 4632 // result of computing the existing spans with the set of spans that 4633 // existed in the history (so that deleting around a span and then 4634 // undoing brings back the span). 4635 function mergeOldSpans(doc, change) { 4636 var old = getOldSpans(doc, change) 4637 var stretched = stretchSpansOverChange(doc, change) 4638 if (!old) { return stretched } 4639 if (!stretched) { return old } 4640 4641 for (var i = 0; i < old.length; ++i) { 4642 var oldCur = old[i], stretchCur = stretched[i] 4643 if (oldCur && stretchCur) { 4644 spans: for (var j = 0; j < stretchCur.length; ++j) { 4645 var span = stretchCur[j] 4646 for (var k = 0; k < oldCur.length; ++k) 4647 { if (oldCur[k].marker == span.marker) { continue spans } } 4648 oldCur.push(span) 4649 } 4650 } else if (stretchCur) { 4651 old[i] = stretchCur 4652 } 4653 } 4654 return old 4655 } 4656 4657 // Used both to provide a JSON-safe object in .getHistory, and, when 4658 // detaching a document, to split the history in two 4659 function copyHistoryArray(events, newGroup, instantiateSel) { 4660 var copy = [] 4661 for (var i = 0; i < events.length; ++i) { 4662 var event = events[i] 4663 if (event.ranges) { 4664 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event) 4665 continue 4666 } 4667 var changes = event.changes, newChanges = [] 4668 copy.push({changes: newChanges}) 4669 for (var j = 0; j < changes.length; ++j) { 4670 var change = changes[j], m = (void 0) 4671 newChanges.push({from: change.from, to: change.to, text: change.text}) 4672 if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { 4673 if (indexOf(newGroup, Number(m[1])) > -1) { 4674 lst(newChanges)[prop] = change[prop] 4675 delete change[prop] 4676 } 4677 } } } 4678 } 4679 } 4680 return copy 4681 } 4682 4683 // The 'scroll' parameter given to many of these indicated whether 4684 // the new cursor position should be scrolled into view after 4685 // modifying the selection. 4686 4687 // If shift is held or the extend flag is set, extends a range to 4688 // include a given position (and optionally a second position). 4689 // Otherwise, simply returns the range between the given positions. 4690 // Used for cursor motion and such. 4691 function extendRange(doc, range, head, other) { 4692 if (doc.cm && doc.cm.display.shift || doc.extend) { 4693 var anchor = range.anchor 4694 if (other) { 4695 var posBefore = cmp(head, anchor) < 0 4696 if (posBefore != (cmp(other, anchor) < 0)) { 4697 anchor = head 4698 head = other 4699 } else if (posBefore != (cmp(head, other) < 0)) { 4700 head = other 4701 } 4702 } 4703 return new Range(anchor, head) 4704 } else { 4705 return new Range(other || head, head) 4706 } 4707 } 4708 4709 // Extend the primary selection range, discard the rest. 4710 function extendSelection(doc, head, other, options) { 4711 setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options) 4712 } 4713 4714 // Extend all selections (pos is an array of selections with length 4715 // equal the number of selections) 4716 function extendSelections(doc, heads, options) { 4717 var out = [] 4718 for (var i = 0; i < doc.sel.ranges.length; i++) 4719 { out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) } 4720 var newSel = normalizeSelection(out, doc.sel.primIndex) 4721 setSelection(doc, newSel, options) 4722 } 4723 4724 // Updates a single range in the selection. 4725 function replaceOneSelection(doc, i, range, options) { 4726 var ranges = doc.sel.ranges.slice(0) 4727 ranges[i] = range 4728 setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options) 4729 } 4730 4731 // Reset the selection to a single range. 4732 function setSimpleSelection(doc, anchor, head, options) { 4733 setSelection(doc, simpleSelection(anchor, head), options) 4734 } 4735 4736 // Give beforeSelectionChange handlers a change to influence a 4737 // selection update. 4738 function filterSelectionChange(doc, sel, options) { 4739 var obj = { 4740 ranges: sel.ranges, 4741 update: function(ranges) { 4742 var this$1 = this; 4743 4744 this.ranges = [] 4745 for (var i = 0; i < ranges.length; i++) 4746 { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), 4747 clipPos(doc, ranges[i].head)) } 4748 }, 4749 origin: options && options.origin 4750 } 4751 signal(doc, "beforeSelectionChange", doc, obj) 4752 if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj) } 4753 if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) } 4754 else { return sel } 4755 } 4756 4757 function setSelectionReplaceHistory(doc, sel, options) { 4758 var done = doc.history.done, last = lst(done) 4759 if (last && last.ranges) { 4760 done[done.length - 1] = sel 4761 setSelectionNoUndo(doc, sel, options) 4762 } else { 4763 setSelection(doc, sel, options) 4764 } 4765 } 4766 4767 // Set a new selection. 4768 function setSelection(doc, sel, options) { 4769 setSelectionNoUndo(doc, sel, options) 4770 addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options) 4771 } 4772 4773 function setSelectionNoUndo(doc, sel, options) { 4774 if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) 4775 { sel = filterSelectionChange(doc, sel, options) } 4776 4777 var bias = options && options.bias || 4778 (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1) 4779 setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)) 4780 4781 if (!(options && options.scroll === false) && doc.cm) 4782 { ensureCursorVisible(doc.cm) } 4783 } 4784 4785 function setSelectionInner(doc, sel) { 4786 if (sel.equals(doc.sel)) { return } 4787 4788 doc.sel = sel 4789 4790 if (doc.cm) { 4791 doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true 4792 signalCursorActivity(doc.cm) 4793 } 4794 signalLater(doc, "cursorActivity", doc) 4795 } 4796 4797 // Verify that the selection does not partially select any atomic 4798 // marked ranges. 4799 function reCheckSelection(doc) { 4800 setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll) 4801 } 4802 4803 // Return a selection that does not partially select any atomic 4804 // ranges. 4805 function skipAtomicInSelection(doc, sel, bias, mayClear) { 4806 var out 4807 for (var i = 0; i < sel.ranges.length; i++) { 4808 var range = sel.ranges[i] 4809 var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i] 4810 var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear) 4811 var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear) 4812 if (out || newAnchor != range.anchor || newHead != range.head) { 4813 if (!out) { out = sel.ranges.slice(0, i) } 4814 out[i] = new Range(newAnchor, newHead) 4815 } 4816 } 4817 return out ? normalizeSelection(out, sel.primIndex) : sel 4818 } 4819 4820 function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { 4821 var line = getLine(doc, pos.line) 4822 if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { 4823 var sp = line.markedSpans[i], m = sp.marker 4824 if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && 4825 (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { 4826 if (mayClear) { 4827 signal(m, "beforeCursorEnter") 4828 if (m.explicitlyCleared) { 4829 if (!line.markedSpans) { break } 4830 else {--i; continue} 4831 } 4832 } 4833 if (!m.atomic) { continue } 4834 4835 if (oldPos) { 4836 var near = m.find(dir < 0 ? 1 : -1), diff = (void 0) 4837 if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) 4838 { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) } 4839 if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) 4840 { return skipAtomicInner(doc, near, pos, dir, mayClear) } 4841 } 4842 4843 var far = m.find(dir < 0 ? -1 : 1) 4844 if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) 4845 { far = movePos(doc, far, dir, far.line == pos.line ? line : null) } 4846 return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null 4847 } 4848 } } 4849 return pos 4850 } 4851 4852 // Ensure a given position is not inside an atomic range. 4853 function skipAtomic(doc, pos, oldPos, bias, mayClear) { 4854 var dir = bias || 1 4855 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || 4856 (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || 4857 skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || 4858 (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)) 4859 if (!found) { 4860 doc.cantEdit = true 4861 return Pos(doc.first, 0) 4862 } 4863 return found 4864 } 4865 4866 function movePos(doc, pos, dir, line) { 4867 if (dir < 0 && pos.ch == 0) { 4868 if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } 4869 else { return null } 4870 } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { 4871 if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } 4872 else { return null } 4873 } else { 4874 return new Pos(pos.line, pos.ch + dir) 4875 } 4876 } 4877 4878 function selectAll(cm) { 4879 cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll) 4880 } 4881 4882 // UPDATING 4883 4884 // Allow "beforeChange" event handlers to influence a change 4885 function filterChange(doc, change, update) { 4886 var obj = { 4887 canceled: false, 4888 from: change.from, 4889 to: change.to, 4890 text: change.text, 4891 origin: change.origin, 4892 cancel: function () { return obj.canceled = true; } 4893 } 4894 if (update) { obj.update = function (from, to, text, origin) { 4895 if (from) { obj.from = clipPos(doc, from) } 4896 if (to) { obj.to = clipPos(doc, to) } 4897 if (text) { obj.text = text } 4898 if (origin !== undefined) { obj.origin = origin } 4899 } } 4900 signal(doc, "beforeChange", doc, obj) 4901 if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj) } 4902 4903 if (obj.canceled) { return null } 4904 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} 4905 } 4906 4907 // Apply a change to a document, and add it to the document's 4908 // history, and propagating it to all linked documents. 4909 function makeChange(doc, change, ignoreReadOnly) { 4910 if (doc.cm) { 4911 if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } 4912 if (doc.cm.state.suppressEdits) { return } 4913 } 4914 4915 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { 4916 change = filterChange(doc, change, true) 4917 if (!change) { return } 4918 } 4919 4920 // Possibly split or suppress the update based on the presence 4921 // of read-only spans in its range. 4922 var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to) 4923 if (split) { 4924 for (var i = split.length - 1; i >= 0; --i) 4925 { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) } 4926 } else { 4927 makeChangeInner(doc, change) 4928 } 4929 } 4930 4931 function makeChangeInner(doc, change) { 4932 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } 4933 var selAfter = computeSelAfterChange(doc, change) 4934 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN) 4935 4936 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)) 4937 var rebased = [] 4938 4939 linkedDocs(doc, function (doc, sharedHist) { 4940 if (!sharedHist && indexOf(rebased, doc.history) == -1) { 4941 rebaseHist(doc.history, change) 4942 rebased.push(doc.history) 4943 } 4944 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)) 4945 }) 4946 } 4947 4948 // Revert a change stored in a document's history. 4949 function makeChangeFromHistory(doc, type, allowSelectionOnly) { 4950 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return } 4951 4952 var hist = doc.history, event, selAfter = doc.sel 4953 var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done 4954 4955 // Verify that there is a useable event (so that ctrl-z won't 4956 // needlessly clear selection events) 4957 var i = 0 4958 for (; i < source.length; i++) { 4959 event = source[i] 4960 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) 4961 { break } 4962 } 4963 if (i == source.length) { return } 4964 hist.lastOrigin = hist.lastSelOrigin = null 4965 4966 for (;;) { 4967 event = source.pop() 4968 if (event.ranges) { 4969 pushSelectionToHistory(event, dest) 4970 if (allowSelectionOnly && !event.equals(doc.sel)) { 4971 setSelection(doc, event, {clearRedo: false}) 4972 return 4973 } 4974 selAfter = event 4975 } 4976 else { break } 4977 } 4978 4979 // Build up a reverse change object to add to the opposite history 4980 // stack (redo when undoing, and vice versa). 4981 var antiChanges = [] 4982 pushSelectionToHistory(selAfter, dest) 4983 dest.push({changes: antiChanges, generation: hist.generation}) 4984 hist.generation = event.generation || ++hist.maxGeneration 4985 4986 var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange") 4987 4988 var loop = function ( i ) { 4989 var change = event.changes[i] 4990 change.origin = type 4991 if (filter && !filterChange(doc, change, false)) { 4992 source.length = 0 4993 return {} 4994 } 4995 4996 antiChanges.push(historyChangeFromChange(doc, change)) 4997 4998 var after = i ? computeSelAfterChange(doc, change) : lst(source) 4999 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)) 5000 if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) } 5001 var rebased = [] 5002 5003 // Propagate to the linked documents 5004 linkedDocs(doc, function (doc, sharedHist) { 5005 if (!sharedHist && indexOf(rebased, doc.history) == -1) { 5006 rebaseHist(doc.history, change) 5007 rebased.push(doc.history) 5008 } 5009 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)) 5010 }) 5011 }; 5012 5013 for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { 5014 var returned = loop( i$1 ); 5015 5016 if ( returned ) return returned.v; 5017 } 5018 } 5019 5020 // Sub-views need their line numbers shifted when text is added 5021 // above or below them in the parent document. 5022 function shiftDoc(doc, distance) { 5023 if (distance == 0) { return } 5024 doc.first += distance 5025 doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( 5026 Pos(range.anchor.line + distance, range.anchor.ch), 5027 Pos(range.head.line + distance, range.head.ch) 5028 ); }), doc.sel.primIndex) 5029 if (doc.cm) { 5030 regChange(doc.cm, doc.first, doc.first - distance, distance) 5031 for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) 5032 { regLineChange(doc.cm, l, "gutter") } 5033 } 5034 } 5035 5036 // More lower-level change function, handling only a single document 5037 // (not linked ones). 5038 function makeChangeSingleDoc(doc, change, selAfter, spans) { 5039 if (doc.cm && !doc.cm.curOp) 5040 { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } 5041 5042 if (change.to.line < doc.first) { 5043 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)) 5044 return 5045 } 5046 if (change.from.line > doc.lastLine()) { return } 5047 5048 // Clip the change to the size of this doc 5049 if (change.from.line < doc.first) { 5050 var shift = change.text.length - 1 - (doc.first - change.from.line) 5051 shiftDoc(doc, shift) 5052 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), 5053 text: [lst(change.text)], origin: change.origin} 5054 } 5055 var last = doc.lastLine() 5056 if (change.to.line > last) { 5057 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), 5058 text: [change.text[0]], origin: change.origin} 5059 } 5060 5061 change.removed = getBetween(doc, change.from, change.to) 5062 5063 if (!selAfter) { selAfter = computeSelAfterChange(doc, change) } 5064 if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans) } 5065 else { updateDoc(doc, change, spans) } 5066 setSelectionNoUndo(doc, selAfter, sel_dontScroll) 5067 } 5068 5069 // Handle the interaction of a change to a document with the editor 5070 // that this document is part of. 5071 function makeChangeSingleDocInEditor(cm, change, spans) { 5072 var doc = cm.doc, display = cm.display, from = change.from, to = change.to 5073 5074 var recomputeMaxLength = false, checkWidthStart = from.line 5075 if (!cm.options.lineWrapping) { 5076 checkWidthStart = lineNo(visualLine(getLine(doc, from.line))) 5077 doc.iter(checkWidthStart, to.line + 1, function (line) { 5078 if (line == display.maxLine) { 5079 recomputeMaxLength = true 5080 return true 5081 } 5082 }) 5083 } 5084 5085 if (doc.sel.contains(change.from, change.to) > -1) 5086 { signalCursorActivity(cm) } 5087 5088 updateDoc(doc, change, spans, estimateHeight(cm)) 5089 5090 if (!cm.options.lineWrapping) { 5091 doc.iter(checkWidthStart, from.line + change.text.length, function (line) { 5092 var len = lineLength(line) 5093 if (len > display.maxLineLength) { 5094 display.maxLine = line 5095 display.maxLineLength = len 5096 display.maxLineChanged = true 5097 recomputeMaxLength = false 5098 } 5099 }) 5100 if (recomputeMaxLength) { cm.curOp.updateMaxLine = true } 5101 } 5102 5103 // Adjust frontier, schedule worker 5104 doc.frontier = Math.min(doc.frontier, from.line) 5105 startWorker(cm, 400) 5106 5107 var lendiff = change.text.length - (to.line - from.line) - 1 5108 // Remember that these lines changed, for updating the display 5109 if (change.full) 5110 { regChange(cm) } 5111 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) 5112 { regLineChange(cm, from.line, "text") } 5113 else 5114 { regChange(cm, from.line, to.line + 1, lendiff) } 5115 5116 var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change") 5117 if (changeHandler || changesHandler) { 5118 var obj = { 5119 from: from, to: to, 5120 text: change.text, 5121 removed: change.removed, 5122 origin: change.origin 5123 } 5124 if (changeHandler) { signalLater(cm, "change", cm, obj) } 5125 if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) } 5126 } 5127 cm.display.selForContextMenu = null 5128 } 5129 5130 function replaceRange(doc, code, from, to, origin) { 5131 if (!to) { to = from } 5132 if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp } 5133 if (typeof code == "string") { code = doc.splitLines(code) } 5134 makeChange(doc, {from: from, to: to, text: code, origin: origin}) 5135 } 5136 5137 // Rebasing/resetting history to deal with externally-sourced changes 5138 5139 function rebaseHistSelSingle(pos, from, to, diff) { 5140 if (to < pos.line) { 5141 pos.line += diff 5142 } else if (from < pos.line) { 5143 pos.line = from 5144 pos.ch = 0 5145 } 5146 } 5147 5148 // Tries to rebase an array of history events given a change in the 5149 // document. If the change touches the same lines as the event, the 5150 // event, and everything 'behind' it, is discarded. If the change is 5151 // before the event, the event's positions are updated. Uses a 5152 // copy-on-write scheme for the positions, to avoid having to 5153 // reallocate them all on every rebase, but also avoid problems with 5154 // shared position objects being unsafely updated. 5155 function rebaseHistArray(array, from, to, diff) { 5156 for (var i = 0; i < array.length; ++i) { 5157 var sub = array[i], ok = true 5158 if (sub.ranges) { 5159 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true } 5160 for (var j = 0; j < sub.ranges.length; j++) { 5161 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff) 5162 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff) 5163 } 5164 continue 5165 } 5166 for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { 5167 var cur = sub.changes[j$1] 5168 if (to < cur.from.line) { 5169 cur.from = Pos(cur.from.line + diff, cur.from.ch) 5170 cur.to = Pos(cur.to.line + diff, cur.to.ch) 5171 } else if (from <= cur.to.line) { 5172 ok = false 5173 break 5174 } 5175 } 5176 if (!ok) { 5177 array.splice(0, i + 1) 5178 i = 0 5179 } 5180 } 5181 } 5182 5183 function rebaseHist(hist, change) { 5184 var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1 5185 rebaseHistArray(hist.done, from, to, diff) 5186 rebaseHistArray(hist.undone, from, to, diff) 5187 } 5188 5189 // Utility for applying a change to a line by handle or number, 5190 // returning the number and optionally registering the line as 5191 // changed. 5192 function changeLine(doc, handle, changeType, op) { 5193 var no = handle, line = handle 5194 if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)) } 5195 else { no = lineNo(handle) } 5196 if (no == null) { return null } 5197 if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType) } 5198 return line 5199 } 5200 5201 // The document is represented as a BTree consisting of leaves, with 5202 // chunk of lines in them, and branches, with up to ten leaves or 5203 // other branch nodes below them. The top node is always a branch 5204 // node, and is the document object itself (meaning it has 5205 // additional methods and properties). 5206 // 5207 // All nodes have parent links. The tree is used both to go from 5208 // line numbers to line objects, and to go from objects to numbers. 5209 // It also indexes by height, and is used to convert between height 5210 // and line object, and to find the total height of the document. 5211 // 5212 // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html 5213 5214 function LeafChunk(lines) { 5215 var this$1 = this; 5216 5217 this.lines = lines 5218 this.parent = null 5219 var height = 0 5220 for (var i = 0; i < lines.length; ++i) { 5221 lines[i].parent = this$1 5222 height += lines[i].height 5223 } 5224 this.height = height 5225 } 5226 5227 LeafChunk.prototype = { 5228 chunkSize: function() { return this.lines.length }, 5229 // Remove the n lines at offset 'at'. 5230 removeInner: function(at, n) { 5231 var this$1 = this; 5232 5233 for (var i = at, e = at + n; i < e; ++i) { 5234 var line = this$1.lines[i] 5235 this$1.height -= line.height 5236 cleanUpLine(line) 5237 signalLater(line, "delete") 5238 } 5239 this.lines.splice(at, n) 5240 }, 5241 // Helper used to collapse a small branch into a single leaf. 5242 collapse: function(lines) { 5243 lines.push.apply(lines, this.lines) 5244 }, 5245 // Insert the given array of lines at offset 'at', count them as 5246 // having the given height. 5247 insertInner: function(at, lines, height) { 5248 var this$1 = this; 5249 5250 this.height += height 5251 this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)) 5252 for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1 } 5253 }, 5254 // Used to iterate over a part of the tree. 5255 iterN: function(at, n, op) { 5256 var this$1 = this; 5257 5258 for (var e = at + n; at < e; ++at) 5259 { if (op(this$1.lines[at])) { return true } } 5260 } 5261 } 5262 5263 function BranchChunk(children) { 5264 var this$1 = this; 5265 5266 this.children = children 5267 var size = 0, height = 0 5268 for (var i = 0; i < children.length; ++i) { 5269 var ch = children[i] 5270 size += ch.chunkSize(); height += ch.height 5271 ch.parent = this$1 5272 } 5273 this.size = size 5274 this.height = height 5275 this.parent = null 5276 } 5277 5278 BranchChunk.prototype = { 5279 chunkSize: function() { return this.size }, 5280 removeInner: function(at, n) { 5281 var this$1 = this; 5282 5283 this.size -= n 5284 for (var i = 0; i < this.children.length; ++i) { 5285 var child = this$1.children[i], sz = child.chunkSize() 5286 if (at < sz) { 5287 var rm = Math.min(n, sz - at), oldHeight = child.height 5288 child.removeInner(at, rm) 5289 this$1.height -= oldHeight - child.height 5290 if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null } 5291 if ((n -= rm) == 0) { break } 5292 at = 0 5293 } else { at -= sz } 5294 } 5295 // If the result is smaller than 25 lines, ensure that it is a 5296 // single leaf node. 5297 if (this.size - n < 25 && 5298 (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { 5299 var lines = [] 5300 this.collapse(lines) 5301 this.children = [new LeafChunk(lines)] 5302 this.children[0].parent = this 5303 } 5304 }, 5305 collapse: function(lines) { 5306 var this$1 = this; 5307 5308 for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines) } 5309 }, 5310 insertInner: function(at, lines, height) { 5311 var this$1 = this; 5312 5313 this.size += lines.length 5314 this.height += height 5315 for (var i = 0; i < this.children.length; ++i) { 5316 var child = this$1.children[i], sz = child.chunkSize() 5317 if (at <= sz) { 5318 child.insertInner(at, lines, height) 5319 if (child.lines && child.lines.length > 50) { 5320 // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. 5321 // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. 5322 var remaining = child.lines.length % 25 + 25 5323 for (var pos = remaining; pos < child.lines.length;) { 5324 var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)) 5325 child.height -= leaf.height 5326 this$1.children.splice(++i, 0, leaf) 5327 leaf.parent = this$1 5328 } 5329 child.lines = child.lines.slice(0, remaining) 5330 this$1.maybeSpill() 5331 } 5332 break 5333 } 5334 at -= sz 5335 } 5336 }, 5337 // When a node has grown, check whether it should be split. 5338 maybeSpill: function() { 5339 if (this.children.length <= 10) { return } 5340 var me = this 5341 do { 5342 var spilled = me.children.splice(me.children.length - 5, 5) 5343 var sibling = new BranchChunk(spilled) 5344 if (!me.parent) { // Become the parent node 5345 var copy = new BranchChunk(me.children) 5346 copy.parent = me 5347 me.children = [copy, sibling] 5348 me = copy 5349 } else { 5350 me.size -= sibling.size 5351 me.height -= sibling.height 5352 var myIndex = indexOf(me.parent.children, me) 5353 me.parent.children.splice(myIndex + 1, 0, sibling) 5354 } 5355 sibling.parent = me.parent 5356 } while (me.children.length > 10) 5357 me.parent.maybeSpill() 5358 }, 5359 iterN: function(at, n, op) { 5360 var this$1 = this; 5361 5362 for (var i = 0; i < this.children.length; ++i) { 5363 var child = this$1.children[i], sz = child.chunkSize() 5364 if (at < sz) { 5365 var used = Math.min(n, sz - at) 5366 if (child.iterN(at, used, op)) { return true } 5367 if ((n -= used) == 0) { break } 5368 at = 0 5369 } else { at -= sz } 5370 } 5371 } 5372 } 5373 5374 // Line widgets are block elements displayed above or below a line. 5375 5376 function LineWidget(doc, node, options) { 5377 var this$1 = this; 5378 5379 if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) 5380 { this$1[opt] = options[opt] } } } 5381 this.doc = doc 5382 this.node = node 5383 } 5384 eventMixin(LineWidget) 5385 5386 function adjustScrollWhenAboveVisible(cm, line, diff) { 5387 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) 5388 { addToScrollPos(cm, null, diff) } 5389 } 5390 5391 LineWidget.prototype.clear = function() { 5392 var this$1 = this; 5393 5394 var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) 5395 if (no == null || !ws) { return } 5396 for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1) } } 5397 if (!ws.length) { line.widgets = null } 5398 var height = widgetHeight(this) 5399 updateLineHeight(line, Math.max(0, line.height - height)) 5400 if (cm) { runInOp(cm, function () { 5401 adjustScrollWhenAboveVisible(cm, line, -height) 5402 regLineChange(cm, no, "widget") 5403 }) } 5404 } 5405 LineWidget.prototype.changed = function() { 5406 var oldH = this.height, cm = this.doc.cm, line = this.line 5407 this.height = null 5408 var diff = widgetHeight(this) - oldH 5409 if (!diff) { return } 5410 updateLineHeight(line, line.height + diff) 5411 if (cm) { runInOp(cm, function () { 5412 cm.curOp.forceUpdate = true 5413 adjustScrollWhenAboveVisible(cm, line, diff) 5414 }) } 5415 } 5416 5417 function addLineWidget(doc, handle, node, options) { 5418 var widget = new LineWidget(doc, node, options) 5419 var cm = doc.cm 5420 if (cm && widget.noHScroll) { cm.display.alignWidgets = true } 5421 changeLine(doc, handle, "widget", function (line) { 5422 var widgets = line.widgets || (line.widgets = []) 5423 if (widget.insertAt == null) { widgets.push(widget) } 5424 else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) } 5425 widget.line = line 5426 if (cm && !lineIsHidden(doc, line)) { 5427 var aboveVisible = heightAtLine(line) < doc.scrollTop 5428 updateLineHeight(line, line.height + widgetHeight(widget)) 5429 if (aboveVisible) { addToScrollPos(cm, null, widget.height) } 5430 cm.curOp.forceUpdate = true 5431 } 5432 return true 5433 }) 5434 return widget 5435 } 5436 5437 // TEXTMARKERS 5438 5439 // Created with markText and setBookmark methods. A TextMarker is a 5440 // handle that can be used to clear or find a marked position in the 5441 // document. Line objects hold arrays (markedSpans) containing 5442 // {from, to, marker} object pointing to such marker objects, and 5443 // indicating that such a marker is present on that line. Multiple 5444 // lines may point to the same marker when it spans across lines. 5445 // The spans will have null for their from/to properties when the 5446 // marker continues beyond the start/end of the line. Markers have 5447 // links back to the lines they currently touch. 5448 5449 // Collapsed markers have unique ids, in order to be able to order 5450 // them, which is needed for uniquely determining an outer marker 5451 // when they overlap (they may nest, but not partially overlap). 5452 var nextMarkerId = 0 5453 5454 function TextMarker(doc, type) { 5455 this.lines = [] 5456 this.type = type 5457 this.doc = doc 5458 this.id = ++nextMarkerId 5459 } 5460 eventMixin(TextMarker) 5461 5462 // Clear the marker. 5463 TextMarker.prototype.clear = function() { 5464 var this$1 = this; 5465 5466 if (this.explicitlyCleared) { return } 5467 var cm = this.doc.cm, withOp = cm && !cm.curOp 5468 if (withOp) { startOperation(cm) } 5469 if (hasHandler(this, "clear")) { 5470 var found = this.find() 5471 if (found) { signalLater(this, "clear", found.from, found.to) } 5472 } 5473 var min = null, max = null 5474 for (var i = 0; i < this.lines.length; ++i) { 5475 var line = this$1.lines[i] 5476 var span = getMarkedSpanFor(line.markedSpans, this$1) 5477 if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text") } 5478 else if (cm) { 5479 if (span.to != null) { max = lineNo(line) } 5480 if (span.from != null) { min = lineNo(line) } 5481 } 5482 line.markedSpans = removeMarkedSpan(line.markedSpans, span) 5483 if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) 5484 { updateLineHeight(line, textHeight(cm.display)) } 5485 } 5486 if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { 5487 var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual) 5488 if (len > cm.display.maxLineLength) { 5489 cm.display.maxLine = visual 5490 cm.display.maxLineLength = len 5491 cm.display.maxLineChanged = true 5492 } 5493 } } 5494 5495 if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1) } 5496 this.lines.length = 0 5497 this.explicitlyCleared = true 5498 if (this.atomic && this.doc.cantEdit) { 5499 this.doc.cantEdit = false 5500 if (cm) { reCheckSelection(cm.doc) } 5501 } 5502 if (cm) { signalLater(cm, "markerCleared", cm, this) } 5503 if (withOp) { endOperation(cm) } 5504 if (this.parent) { this.parent.clear() } 5505 } 5506 5507 // Find the position of the marker in the document. Returns a {from, 5508 // to} object by default. Side can be passed to get a specific side 5509 // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the 5510 // Pos objects returned contain a line object, rather than a line 5511 // number (used to prevent looking up the same line twice). 5512 TextMarker.prototype.find = function(side, lineObj) { 5513 var this$1 = this; 5514 5515 if (side == null && this.type == "bookmark") { side = 1 } 5516 var from, to 5517 for (var i = 0; i < this.lines.length; ++i) { 5518 var line = this$1.lines[i] 5519 var span = getMarkedSpanFor(line.markedSpans, this$1) 5520 if (span.from != null) { 5521 from = Pos(lineObj ? line : lineNo(line), span.from) 5522 if (side == -1) { return from } 5523 } 5524 if (span.to != null) { 5525 to = Pos(lineObj ? line : lineNo(line), span.to) 5526 if (side == 1) { return to } 5527 } 5528 } 5529 return from && {from: from, to: to} 5530 } 5531 5532 // Signals that the marker's widget changed, and surrounding layout 5533 // should be recomputed. 5534 TextMarker.prototype.changed = function() { 5535 var pos = this.find(-1, true), widget = this, cm = this.doc.cm 5536 if (!pos || !cm) { return } 5537 runInOp(cm, function () { 5538 var line = pos.line, lineN = lineNo(pos.line) 5539 var view = findViewForLine(cm, lineN) 5540 if (view) { 5541 clearLineMeasurementCacheFor(view) 5542 cm.curOp.selectionChanged = cm.curOp.forceUpdate = true 5543 } 5544 cm.curOp.updateMaxLine = true 5545 if (!lineIsHidden(widget.doc, line) && widget.height != null) { 5546 var oldHeight = widget.height 5547 widget.height = null 5548 var dHeight = widgetHeight(widget) - oldHeight 5549 if (dHeight) 5550 { updateLineHeight(line, line.height + dHeight) } 5551 } 5552 }) 5553 } 5554 5555 TextMarker.prototype.attachLine = function(line) { 5556 if (!this.lines.length && this.doc.cm) { 5557 var op = this.doc.cm.curOp 5558 if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) 5559 { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) } 5560 } 5561 this.lines.push(line) 5562 } 5563 TextMarker.prototype.detachLine = function(line) { 5564 this.lines.splice(indexOf(this.lines, line), 1) 5565 if (!this.lines.length && this.doc.cm) { 5566 var op = this.doc.cm.curOp 5567 ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) 5568 } 5569 } 5570 5571 // Create a marker, wire it up to the right lines, and 5572 function markText(doc, from, to, options, type) { 5573 // Shared markers (across linked documents) are handled separately 5574 // (markTextShared will call out to this again, once per 5575 // document). 5576 if (options && options.shared) { return markTextShared(doc, from, to, options, type) } 5577 // Ensure we are in an operation. 5578 if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } 5579 5580 var marker = new TextMarker(doc, type), diff = cmp(from, to) 5581 if (options) { copyObj(options, marker, false) } 5582 // Don't connect empty markers unless clearWhenEmpty is false 5583 if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) 5584 { return marker } 5585 if (marker.replacedWith) { 5586 // Showing up as a widget implies collapsed (widget replaces text) 5587 marker.collapsed = true 5588 marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget") 5589 marker.widgetNode.setAttribute("role", "presentation") // hide from accessibility tree 5590 if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true") } 5591 if (options.insertLeft) { marker.widgetNode.insertLeft = true } 5592 } 5593 if (marker.collapsed) { 5594 if (conflictingCollapsedRange(doc, from.line, from, to, marker) || 5595 from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) 5596 { throw new Error("Inserting collapsed marker partially overlapping an existing one") } 5597 seeCollapsedSpans() 5598 } 5599 5600 if (marker.addToHistory) 5601 { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) } 5602 5603 var curLine = from.line, cm = doc.cm, updateMaxLine 5604 doc.iter(curLine, to.line + 1, function (line) { 5605 if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) 5606 { updateMaxLine = true } 5607 if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0) } 5608 addMarkedSpan(line, new MarkedSpan(marker, 5609 curLine == from.line ? from.ch : null, 5610 curLine == to.line ? to.ch : null)) 5611 ++curLine 5612 }) 5613 // lineIsHidden depends on the presence of the spans, so needs a second pass 5614 if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { 5615 if (lineIsHidden(doc, line)) { updateLineHeight(line, 0) } 5616 }) } 5617 5618 if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }) } 5619 5620 if (marker.readOnly) { 5621 seeReadOnlySpans() 5622 if (doc.history.done.length || doc.history.undone.length) 5623 { doc.clearHistory() } 5624 } 5625 if (marker.collapsed) { 5626 marker.id = ++nextMarkerId 5627 marker.atomic = true 5628 } 5629 if (cm) { 5630 // Sync editor state 5631 if (updateMaxLine) { cm.curOp.updateMaxLine = true } 5632 if (marker.collapsed) 5633 { regChange(cm, from.line, to.line + 1) } 5634 else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) 5635 { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text") } } 5636 if (marker.atomic) { reCheckSelection(cm.doc) } 5637 signalLater(cm, "markerAdded", cm, marker) 5638 } 5639 return marker 5640 } 5641 5642 // SHARED TEXTMARKERS 5643 5644 // A shared marker spans multiple linked documents. It is 5645 // implemented as a meta-marker-object controlling multiple normal 5646 // markers. 5647 function SharedTextMarker(markers, primary) { 5648 var this$1 = this; 5649 5650 this.markers = markers 5651 this.primary = primary 5652 for (var i = 0; i < markers.length; ++i) 5653 { markers[i].parent = this$1 } 5654 } 5655 eventMixin(SharedTextMarker) 5656 5657 SharedTextMarker.prototype.clear = function() { 5658 var this$1 = this; 5659 5660 if (this.explicitlyCleared) { return } 5661 this.explicitlyCleared = true 5662 for (var i = 0; i < this.markers.length; ++i) 5663 { this$1.markers[i].clear() } 5664 signalLater(this, "clear") 5665 } 5666 SharedTextMarker.prototype.find = function(side, lineObj) { 5667 return this.primary.find(side, lineObj) 5668 } 5669 5670 function markTextShared(doc, from, to, options, type) { 5671 options = copyObj(options) 5672 options.shared = false 5673 var markers = [markText(doc, from, to, options, type)], primary = markers[0] 5674 var widget = options.widgetNode 5675 linkedDocs(doc, function (doc) { 5676 if (widget) { options.widgetNode = widget.cloneNode(true) } 5677 markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)) 5678 for (var i = 0; i < doc.linked.length; ++i) 5679 { if (doc.linked[i].isParent) { return } } 5680 primary = lst(markers) 5681 }) 5682 return new SharedTextMarker(markers, primary) 5683 } 5684 5685 function findSharedMarkers(doc) { 5686 return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) 5687 } 5688 5689 function copySharedMarkers(doc, markers) { 5690 for (var i = 0; i < markers.length; i++) { 5691 var marker = markers[i], pos = marker.find() 5692 var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to) 5693 if (cmp(mFrom, mTo)) { 5694 var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type) 5695 marker.markers.push(subMark) 5696 subMark.parent = marker 5697 } 5698 } 5699 } 5700 5701 function detachSharedMarkers(markers) { 5702 var loop = function ( i ) { 5703 var marker = markers[i], linked = [marker.primary.doc] 5704 linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }) 5705 for (var j = 0; j < marker.markers.length; j++) { 5706 var subMarker = marker.markers[j] 5707 if (indexOf(linked, subMarker.doc) == -1) { 5708 subMarker.parent = null 5709 marker.markers.splice(j--, 1) 5710 } 5711 } 5712 }; 5713 5714 for (var i = 0; i < markers.length; i++) loop( i ); 5715 } 5716 5717 var nextDocId = 0 5718 var Doc = function(text, mode, firstLine, lineSep) { 5719 if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep) } 5720 if (firstLine == null) { firstLine = 0 } 5721 5722 BranchChunk.call(this, [new LeafChunk([new Line("", null)])]) 5723 this.first = firstLine 5724 this.scrollTop = this.scrollLeft = 0 5725 this.cantEdit = false 5726 this.cleanGeneration = 1 5727 this.frontier = firstLine 5728 var start = Pos(firstLine, 0) 5729 this.sel = simpleSelection(start) 5730 this.history = new History(null) 5731 this.id = ++nextDocId 5732 this.modeOption = mode 5733 this.lineSep = lineSep 5734 this.extend = false 5735 5736 if (typeof text == "string") { text = this.splitLines(text) } 5737 updateDoc(this, {from: start, to: start, text: text}) 5738 setSelection(this, simpleSelection(start), sel_dontScroll) 5739 } 5740 5741 Doc.prototype = createObj(BranchChunk.prototype, { 5742 constructor: Doc, 5743 // Iterate over the document. Supports two forms -- with only one 5744 // argument, it calls that for each line in the document. With 5745 // three, it iterates over the range given by the first two (with 5746 // the second being non-inclusive). 5747 iter: function(from, to, op) { 5748 if (op) { this.iterN(from - this.first, to - from, op) } 5749 else { this.iterN(this.first, this.first + this.size, from) } 5750 }, 5751 5752 // Non-public interface for adding and removing lines. 5753 insert: function(at, lines) { 5754 var height = 0 5755 for (var i = 0; i < lines.length; ++i) { height += lines[i].height } 5756 this.insertInner(at - this.first, lines, height) 5757 }, 5758 remove: function(at, n) { this.removeInner(at - this.first, n) }, 5759 5760 // From here, the methods are part of the public interface. Most 5761 // are also available from CodeMirror (editor) instances. 5762 5763 getValue: function(lineSep) { 5764 var lines = getLines(this, this.first, this.first + this.size) 5765 if (lineSep === false) { return lines } 5766 return lines.join(lineSep || this.lineSeparator()) 5767 }, 5768 setValue: docMethodOp(function(code) { 5769 var top = Pos(this.first, 0), last = this.first + this.size - 1 5770 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), 5771 text: this.splitLines(code), origin: "setValue", full: true}, true) 5772 setSelection(this, simpleSelection(top)) 5773 }), 5774 replaceRange: function(code, from, to, origin) { 5775 from = clipPos(this, from) 5776 to = to ? clipPos(this, to) : from 5777 replaceRange(this, code, from, to, origin) 5778 }, 5779 getRange: function(from, to, lineSep) { 5780 var lines = getBetween(this, clipPos(this, from), clipPos(this, to)) 5781 if (lineSep === false) { return lines } 5782 return lines.join(lineSep || this.lineSeparator()) 5783 }, 5784 5785 getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, 5786 5787 getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, 5788 getLineNumber: function(line) {return lineNo(line)}, 5789 5790 getLineHandleVisualStart: function(line) { 5791 if (typeof line == "number") { line = getLine(this, line) } 5792 return visualLine(line) 5793 }, 5794 5795 lineCount: function() {return this.size}, 5796 firstLine: function() {return this.first}, 5797 lastLine: function() {return this.first + this.size - 1}, 5798 5799 clipPos: function(pos) {return clipPos(this, pos)}, 5800 5801 getCursor: function(start) { 5802 var range = this.sel.primary(), pos 5803 if (start == null || start == "head") { pos = range.head } 5804 else if (start == "anchor") { pos = range.anchor } 5805 else if (start == "end" || start == "to" || start === false) { pos = range.to() } 5806 else { pos = range.from() } 5807 return pos 5808 }, 5809 listSelections: function() { return this.sel.ranges }, 5810 somethingSelected: function() {return this.sel.somethingSelected()}, 5811 5812 setCursor: docMethodOp(function(line, ch, options) { 5813 setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options) 5814 }), 5815 setSelection: docMethodOp(function(anchor, head, options) { 5816 setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options) 5817 }), 5818 extendSelection: docMethodOp(function(head, other, options) { 5819 extendSelection(this, clipPos(this, head), other && clipPos(this, other), options) 5820 }), 5821 extendSelections: docMethodOp(function(heads, options) { 5822 extendSelections(this, clipPosArray(this, heads), options) 5823 }), 5824 extendSelectionsBy: docMethodOp(function(f, options) { 5825 var heads = map(this.sel.ranges, f) 5826 extendSelections(this, clipPosArray(this, heads), options) 5827 }), 5828 setSelections: docMethodOp(function(ranges, primary, options) { 5829 var this$1 = this; 5830 5831 if (!ranges.length) { return } 5832 var out = [] 5833 for (var i = 0; i < ranges.length; i++) 5834 { out[i] = new Range(clipPos(this$1, ranges[i].anchor), 5835 clipPos(this$1, ranges[i].head)) } 5836 if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex) } 5837 setSelection(this, normalizeSelection(out, primary), options) 5838 }), 5839 addSelection: docMethodOp(function(anchor, head, options) { 5840 var ranges = this.sel.ranges.slice(0) 5841 ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))) 5842 setSelection(this, normalizeSelection(ranges, ranges.length - 1), options) 5843 }), 5844 5845 getSelection: function(lineSep) { 5846 var this$1 = this; 5847 5848 var ranges = this.sel.ranges, lines 5849 for (var i = 0; i < ranges.length; i++) { 5850 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()) 5851 lines = lines ? lines.concat(sel) : sel 5852 } 5853 if (lineSep === false) { return lines } 5854 else { return lines.join(lineSep || this.lineSeparator()) } 5855 }, 5856 getSelections: function(lineSep) { 5857 var this$1 = this; 5858 5859 var parts = [], ranges = this.sel.ranges 5860 for (var i = 0; i < ranges.length; i++) { 5861 var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()) 5862 if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()) } 5863 parts[i] = sel 5864 } 5865 return parts 5866 }, 5867 replaceSelection: function(code, collapse, origin) { 5868 var dup = [] 5869 for (var i = 0; i < this.sel.ranges.length; i++) 5870 { dup[i] = code } 5871 this.replaceSelections(dup, collapse, origin || "+input") 5872 }, 5873 replaceSelections: docMethodOp(function(code, collapse, origin) { 5874 var this$1 = this; 5875 5876 var changes = [], sel = this.sel 5877 for (var i = 0; i < sel.ranges.length; i++) { 5878 var range = sel.ranges[i] 5879 changes[i] = {from: range.from(), to: range.to(), text: this$1.splitLines(code[i]), origin: origin} 5880 } 5881 var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse) 5882 for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) 5883 { makeChange(this$1, changes[i$1]) } 5884 if (newSel) { setSelectionReplaceHistory(this, newSel) } 5885 else if (this.cm) { ensureCursorVisible(this.cm) } 5886 }), 5887 undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}), 5888 redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}), 5889 undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}), 5890 redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}), 5891 5892 setExtending: function(val) {this.extend = val}, 5893 getExtending: function() {return this.extend}, 5894 5895 historySize: function() { 5896 var hist = this.history, done = 0, undone = 0 5897 for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done } } 5898 for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone } } 5899 return {undo: done, redo: undone} 5900 }, 5901 clearHistory: function() {this.history = new History(this.history.maxGeneration)}, 5902 5903 markClean: function() { 5904 this.cleanGeneration = this.changeGeneration(true) 5905 }, 5906 changeGeneration: function(forceSplit) { 5907 if (forceSplit) 5908 { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null } 5909 return this.history.generation 5910 }, 5911 isClean: function (gen) { 5912 return this.history.generation == (gen || this.cleanGeneration) 5913 }, 5914 5915 getHistory: function() { 5916 return {done: copyHistoryArray(this.history.done), 5917 undone: copyHistoryArray(this.history.undone)} 5918 }, 5919 setHistory: function(histData) { 5920 var hist = this.history = new History(this.history.maxGeneration) 5921 hist.done = copyHistoryArray(histData.done.slice(0), null, true) 5922 hist.undone = copyHistoryArray(histData.undone.slice(0), null, true) 5923 }, 5924 5925 setGutterMarker: docMethodOp(function(line, gutterID, value) { 5926 return changeLine(this, line, "gutter", function (line) { 5927 var markers = line.gutterMarkers || (line.gutterMarkers = {}) 5928 markers[gutterID] = value 5929 if (!value && isEmpty(markers)) { line.gutterMarkers = null } 5930 return true 5931 }) 5932 }), 5933 5934 clearGutter: docMethodOp(function(gutterID) { 5935 var this$1 = this; 5936 5937 this.iter(function (line) { 5938 if (line.gutterMarkers && line.gutterMarkers[gutterID]) { 5939 changeLine(this$1, line, "gutter", function () { 5940 line.gutterMarkers[gutterID] = null 5941 if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null } 5942 return true 5943 }) 5944 } 5945 }) 5946 }), 5947 5948 lineInfo: function(line) { 5949 var n 5950 if (typeof line == "number") { 5951 if (!isLine(this, line)) { return null } 5952 n = line 5953 line = getLine(this, line) 5954 if (!line) { return null } 5955 } else { 5956 n = lineNo(line) 5957 if (n == null) { return null } 5958 } 5959 return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, 5960 textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, 5961 widgets: line.widgets} 5962 }, 5963 5964 addLineClass: docMethodOp(function(handle, where, cls) { 5965 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { 5966 var prop = where == "text" ? "textClass" 5967 : where == "background" ? "bgClass" 5968 : where == "gutter" ? "gutterClass" : "wrapClass" 5969 if (!line[prop]) { line[prop] = cls } 5970 else if (classTest(cls).test(line[prop])) { return false } 5971 else { line[prop] += " " + cls } 5972 return true 5973 }) 5974 }), 5975 removeLineClass: docMethodOp(function(handle, where, cls) { 5976 return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { 5977 var prop = where == "text" ? "textClass" 5978 : where == "background" ? "bgClass" 5979 : where == "gutter" ? "gutterClass" : "wrapClass" 5980 var cur = line[prop] 5981 if (!cur) { return false } 5982 else if (cls == null) { line[prop] = null } 5983 else { 5984 var found = cur.match(classTest(cls)) 5985 if (!found) { return false } 5986 var end = found.index + found[0].length 5987 line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null 5988 } 5989 return true 5990 }) 5991 }), 5992 5993 addLineWidget: docMethodOp(function(handle, node, options) { 5994 return addLineWidget(this, handle, node, options) 5995 }), 5996 removeLineWidget: function(widget) { widget.clear() }, 5997 5998 markText: function(from, to, options) { 5999 return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") 6000 }, 6001 setBookmark: function(pos, options) { 6002 var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), 6003 insertLeft: options && options.insertLeft, 6004 clearWhenEmpty: false, shared: options && options.shared, 6005 handleMouseEvents: options && options.handleMouseEvents} 6006 pos = clipPos(this, pos) 6007 return markText(this, pos, pos, realOpts, "bookmark") 6008 }, 6009 findMarksAt: function(pos) { 6010 pos = clipPos(this, pos) 6011 var markers = [], spans = getLine(this, pos.line).markedSpans 6012 if (spans) { for (var i = 0; i < spans.length; ++i) { 6013 var span = spans[i] 6014 if ((span.from == null || span.from <= pos.ch) && 6015 (span.to == null || span.to >= pos.ch)) 6016 { markers.push(span.marker.parent || span.marker) } 6017 } } 6018 return markers 6019 }, 6020 findMarks: function(from, to, filter) { 6021 from = clipPos(this, from); to = clipPos(this, to) 6022 var found = [], lineNo = from.line 6023 this.iter(from.line, to.line + 1, function (line) { 6024 var spans = line.markedSpans 6025 if (spans) { for (var i = 0; i < spans.length; i++) { 6026 var span = spans[i] 6027 if (!(span.to != null && lineNo == from.line && from.ch >= span.to || 6028 span.from == null && lineNo != from.line || 6029 span.from != null && lineNo == to.line && span.from >= to.ch) && 6030 (!filter || filter(span.marker))) 6031 { found.push(span.marker.parent || span.marker) } 6032 } } 6033 ++lineNo 6034 }) 6035 return found 6036 }, 6037 getAllMarks: function() { 6038 var markers = [] 6039 this.iter(function (line) { 6040 var sps = line.markedSpans 6041 if (sps) { for (var i = 0; i < sps.length; ++i) 6042 { if (sps[i].from != null) { markers.push(sps[i].marker) } } } 6043 }) 6044 return markers 6045 }, 6046 6047 posFromIndex: function(off) { 6048 var ch, lineNo = this.first, sepSize = this.lineSeparator().length 6049 this.iter(function (line) { 6050 var sz = line.text.length + sepSize 6051 if (sz > off) { ch = off; return true } 6052 off -= sz 6053 ++lineNo 6054 }) 6055 return clipPos(this, Pos(lineNo, ch)) 6056 }, 6057 indexFromPos: function (coords) { 6058 coords = clipPos(this, coords) 6059 var index = coords.ch 6060 if (coords.line < this.first || coords.ch < 0) { return 0 } 6061 var sepSize = this.lineSeparator().length 6062 this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value 6063 index += line.text.length + sepSize 6064 }) 6065 return index 6066 }, 6067 6068 copy: function(copyHistory) { 6069 var doc = new Doc(getLines(this, this.first, this.first + this.size), 6070 this.modeOption, this.first, this.lineSep) 6071 doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft 6072 doc.sel = this.sel 6073 doc.extend = false 6074 if (copyHistory) { 6075 doc.history.undoDepth = this.history.undoDepth 6076 doc.setHistory(this.getHistory()) 6077 } 6078 return doc 6079 }, 6080 6081 linkedDoc: function(options) { 6082 if (!options) { options = {} } 6083 var from = this.first, to = this.first + this.size 6084 if (options.from != null && options.from > from) { from = options.from } 6085 if (options.to != null && options.to < to) { to = options.to } 6086 var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep) 6087 if (options.sharedHist) { copy.history = this.history 6088 ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}) 6089 copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}] 6090 copySharedMarkers(copy, findSharedMarkers(this)) 6091 return copy 6092 }, 6093 unlinkDoc: function(other) { 6094 var this$1 = this; 6095 6096 if (other instanceof CodeMirror) { other = other.doc } 6097 if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { 6098 var link = this$1.linked[i] 6099 if (link.doc != other) { continue } 6100 this$1.linked.splice(i, 1) 6101 other.unlinkDoc(this$1) 6102 detachSharedMarkers(findSharedMarkers(this$1)) 6103 break 6104 } } 6105 // If the histories were shared, split them again 6106 if (other.history == this.history) { 6107 var splitIds = [other.id] 6108 linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true) 6109 other.history = new History(null) 6110 other.history.done = copyHistoryArray(this.history.done, splitIds) 6111 other.history.undone = copyHistoryArray(this.history.undone, splitIds) 6112 } 6113 }, 6114 iterLinkedDocs: function(f) {linkedDocs(this, f)}, 6115 6116 getMode: function() {return this.mode}, 6117 getEditor: function() {return this.cm}, 6118 6119 splitLines: function(str) { 6120 if (this.lineSep) { return str.split(this.lineSep) } 6121 return splitLinesAuto(str) 6122 }, 6123 lineSeparator: function() { return this.lineSep || "\n" } 6124 }) 6125 6126 // Public alias. 6127 Doc.prototype.eachLine = Doc.prototype.iter 6128 6129 // Kludge to work around strange IE behavior where it'll sometimes 6130 // re-fire a series of drag-related events right after the drop (#1551) 6131 var lastDrop = 0 6132 6133 function onDrop(e) { 6134 var cm = this 6135 clearDragCursor(cm) 6136 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) 6137 { return } 6138 e_preventDefault(e) 6139 if (ie) { lastDrop = +new Date } 6140 var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files 6141 if (!pos || cm.isReadOnly()) { return } 6142 // Might be a file drop, in which case we simply extract the text 6143 // and insert it. 6144 if (files && files.length && window.FileReader && window.File) { 6145 var n = files.length, text = Array(n), read = 0 6146 var loadFile = function (file, i) { 6147 if (cm.options.allowDropFileTypes && 6148 indexOf(cm.options.allowDropFileTypes, file.type) == -1) 6149 { return } 6150 6151 var reader = new FileReader 6152 reader.onload = operation(cm, function () { 6153 var content = reader.result 6154 if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = "" } 6155 text[i] = content 6156 if (++read == n) { 6157 pos = clipPos(cm.doc, pos) 6158 var change = {from: pos, to: pos, 6159 text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), 6160 origin: "paste"} 6161 makeChange(cm.doc, change) 6162 setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))) 6163 } 6164 }) 6165 reader.readAsText(file) 6166 } 6167 for (var i = 0; i < n; ++i) { loadFile(files[i], i) } 6168 } else { // Normal drop 6169 // Don't do a replace if the drop happened inside of the selected text. 6170 if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { 6171 cm.state.draggingText(e) 6172 // Ensure the editor is re-focused 6173 setTimeout(function () { return cm.display.input.focus(); }, 20) 6174 return 6175 } 6176 try { 6177 var text$1 = e.dataTransfer.getData("Text") 6178 if (text$1) { 6179 var selected 6180 if (cm.state.draggingText && !cm.state.draggingText.copy) 6181 { selected = cm.listSelections() } 6182 setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)) 6183 if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) 6184 { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag") } } 6185 cm.replaceSelection(text$1, "around", "paste") 6186 cm.display.input.focus() 6187 } 6188 } 6189 catch(e){} 6190 } 6191 } 6192 6193 function onDragStart(cm, e) { 6194 if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } 6195 if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } 6196 6197 e.dataTransfer.setData("Text", cm.getSelection()) 6198 e.dataTransfer.effectAllowed = "copyMove" 6199 6200 // Use dummy image instead of default browsers image. 6201 // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. 6202 if (e.dataTransfer.setDragImage && !safari) { 6203 var img = elt("img", null, null, "position: fixed; left: 0; top: 0;") 6204 img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" 6205 if (presto) { 6206 img.width = img.height = 1 6207 cm.display.wrapper.appendChild(img) 6208 // Force a relayout, or Opera won't use our image for some obscure reason 6209 img._top = img.offsetTop 6210 } 6211 e.dataTransfer.setDragImage(img, 0, 0) 6212 if (presto) { img.parentNode.removeChild(img) } 6213 } 6214 } 6215 6216 function onDragOver(cm, e) { 6217 var pos = posFromMouse(cm, e) 6218 if (!pos) { return } 6219 var frag = document.createDocumentFragment() 6220 drawSelectionCursor(cm, pos, frag) 6221 if (!cm.display.dragCursor) { 6222 cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors") 6223 cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv) 6224 } 6225 removeChildrenAndAdd(cm.display.dragCursor, frag) 6226 } 6227 6228 function clearDragCursor(cm) { 6229 if (cm.display.dragCursor) { 6230 cm.display.lineSpace.removeChild(cm.display.dragCursor) 6231 cm.display.dragCursor = null 6232 } 6233 } 6234 6235 // These must be handled carefully, because naively registering a 6236 // handler for each editor will cause the editors to never be 6237 // garbage collected. 6238 6239 function forEachCodeMirror(f) { 6240 if (!document.body.getElementsByClassName) { return } 6241 var byClass = document.body.getElementsByClassName("CodeMirror") 6242 for (var i = 0; i < byClass.length; i++) { 6243 var cm = byClass[i].CodeMirror 6244 if (cm) { f(cm) } 6245 } 6246 } 6247 6248 var globalsRegistered = false 6249 function ensureGlobalHandlers() { 6250 if (globalsRegistered) { return } 6251 registerGlobalHandlers() 6252 globalsRegistered = true 6253 } 6254 function registerGlobalHandlers() { 6255 // When the window resizes, we need to refresh active editors. 6256 var resizeTimer 6257 on(window, "resize", function () { 6258 if (resizeTimer == null) { resizeTimer = setTimeout(function () { 6259 resizeTimer = null 6260 forEachCodeMirror(onResize) 6261 }, 100) } 6262 }) 6263 // When the window loses focus, we want to show the editor as blurred 6264 on(window, "blur", function () { return forEachCodeMirror(onBlur); }) 6265 } 6266 // Called when the window resizes 6267 function onResize(cm) { 6268 var d = cm.display 6269 if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) 6270 { return } 6271 // Might be a text scaling operation, clear size caches. 6272 d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null 6273 d.scrollbarsClipped = false 6274 cm.setSize() 6275 } 6276 6277 var keyNames = { 6278 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 6279 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 6280 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 6281 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 6282 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 6283 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 6284 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 6285 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" 6286 } 6287 6288 // Number keys 6289 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) } 6290 // Alphabetic keys 6291 for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) } 6292 // Function keys 6293 for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 } 6294 6295 var keyMap = {} 6296 6297 keyMap.basic = { 6298 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", 6299 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", 6300 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", 6301 "Tab": "defaultTab", "Shift-Tab": "indentAuto", 6302 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", 6303 "Esc": "singleSelection" 6304 } 6305 // Note that the save and find-related commands aren't defined by 6306 // default. User code or addons can define them. Unknown commands 6307 // are simply ignored. 6308 keyMap.pcDefault = { 6309 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", 6310 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", 6311 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", 6312 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", 6313 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", 6314 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", 6315 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", 6316 fallthrough: "basic" 6317 } 6318 // Very basic readline/emacs-style bindings, which are standard on Mac. 6319 keyMap.emacsy = { 6320 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", 6321 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", 6322 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", 6323 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", 6324 "Ctrl-O": "openLine" 6325 } 6326 keyMap.macDefault = { 6327 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", 6328 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", 6329 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", 6330 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", 6331 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", 6332 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", 6333 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", 6334 fallthrough: ["basic", "emacsy"] 6335 } 6336 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault 6337 6338 // KEYMAP DISPATCH 6339 6340 function normalizeKeyName(name) { 6341 var parts = name.split(/-(?!$)/) 6342 name = parts[parts.length - 1] 6343 var alt, ctrl, shift, cmd 6344 for (var i = 0; i < parts.length - 1; i++) { 6345 var mod = parts[i] 6346 if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true } 6347 else if (/^a(lt)?$/i.test(mod)) { alt = true } 6348 else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true } 6349 else if (/^s(hift)?$/i.test(mod)) { shift = true } 6350 else { throw new Error("Unrecognized modifier name: " + mod) } 6351 } 6352 if (alt) { name = "Alt-" + name } 6353 if (ctrl) { name = "Ctrl-" + name } 6354 if (cmd) { name = "Cmd-" + name } 6355 if (shift) { name = "Shift-" + name } 6356 return name 6357 } 6358 6359 // This is a kludge to keep keymaps mostly working as raw objects 6360 // (backwards compatibility) while at the same time support features 6361 // like normalization and multi-stroke key bindings. It compiles a 6362 // new normalized keymap, and then updates the old object to reflect 6363 // this. 6364 function normalizeKeyMap(keymap) { 6365 var copy = {} 6366 for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { 6367 var value = keymap[keyname] 6368 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } 6369 if (value == "...") { delete keymap[keyname]; continue } 6370 6371 var keys = map(keyname.split(" "), normalizeKeyName) 6372 for (var i = 0; i < keys.length; i++) { 6373 var val = (void 0), name = (void 0) 6374 if (i == keys.length - 1) { 6375 name = keys.join(" ") 6376 val = value 6377 } else { 6378 name = keys.slice(0, i + 1).join(" ") 6379 val = "..." 6380 } 6381 var prev = copy[name] 6382 if (!prev) { copy[name] = val } 6383 else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } 6384 } 6385 delete keymap[keyname] 6386 } } 6387 for (var prop in copy) { keymap[prop] = copy[prop] } 6388 return keymap 6389 } 6390 6391 function lookupKey(key, map, handle, context) { 6392 map = getKeyMap(map) 6393 var found = map.call ? map.call(key, context) : map[key] 6394 if (found === false) { return "nothing" } 6395 if (found === "...") { return "multi" } 6396 if (found != null && handle(found)) { return "handled" } 6397 6398 if (map.fallthrough) { 6399 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") 6400 { return lookupKey(key, map.fallthrough, handle, context) } 6401 for (var i = 0; i < map.fallthrough.length; i++) { 6402 var result = lookupKey(key, map.fallthrough[i], handle, context) 6403 if (result) { return result } 6404 } 6405 } 6406 } 6407 6408 // Modifier key presses don't count as 'real' key presses for the 6409 // purpose of keymap fallthrough. 6410 function isModifierKey(value) { 6411 var name = typeof value == "string" ? value : keyNames[value.keyCode] 6412 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" 6413 } 6414 6415 // Look up the name of a key as indicated by an event object. 6416 function keyName(event, noShift) { 6417 if (presto && event.keyCode == 34 && event["char"]) { return false } 6418 var base = keyNames[event.keyCode], name = base 6419 if (name == null || event.altGraphKey) { return false } 6420 if (event.altKey && base != "Alt") { name = "Alt-" + name } 6421 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name } 6422 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name } 6423 if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name } 6424 return name 6425 } 6426 6427 function getKeyMap(val) { 6428 return typeof val == "string" ? keyMap[val] : val 6429 } 6430 6431 // Helper for deleting text near the selection(s), used to implement 6432 // backspace, delete, and similar functionality. 6433 function deleteNearSelection(cm, compute) { 6434 var ranges = cm.doc.sel.ranges, kill = [] 6435 // Build up a set of ranges to kill first, merging overlapping 6436 // ranges. 6437 for (var i = 0; i < ranges.length; i++) { 6438 var toKill = compute(ranges[i]) 6439 while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { 6440 var replaced = kill.pop() 6441 if (cmp(replaced.from, toKill.from) < 0) { 6442 toKill.from = replaced.from 6443 break 6444 } 6445 } 6446 kill.push(toKill) 6447 } 6448 // Next, remove those actual ranges. 6449 runInOp(cm, function () { 6450 for (var i = kill.length - 1; i >= 0; i--) 6451 { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") } 6452 ensureCursorVisible(cm) 6453 }) 6454 } 6455 6456 // Commands are parameter-less actions that can be performed on an 6457 // editor, mostly used for keybindings. 6458 var commands = { 6459 selectAll: selectAll, 6460 singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, 6461 killLine: function (cm) { return deleteNearSelection(cm, function (range) { 6462 if (range.empty()) { 6463 var len = getLine(cm.doc, range.head.line).text.length 6464 if (range.head.ch == len && range.head.line < cm.lastLine()) 6465 { return {from: range.head, to: Pos(range.head.line + 1, 0)} } 6466 else 6467 { return {from: range.head, to: Pos(range.head.line, len)} } 6468 } else { 6469 return {from: range.from(), to: range.to()} 6470 } 6471 }); }, 6472 deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ 6473 from: Pos(range.from().line, 0), 6474 to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) 6475 }); }); }, 6476 delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ 6477 from: Pos(range.from().line, 0), to: range.from() 6478 }); }); }, 6479 delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { 6480 var top = cm.charCoords(range.head, "div").top + 5 6481 var leftPos = cm.coordsChar({left: 0, top: top}, "div") 6482 return {from: leftPos, to: range.from()} 6483 }); }, 6484 delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { 6485 var top = cm.charCoords(range.head, "div").top + 5 6486 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") 6487 return {from: range.from(), to: rightPos } 6488 }); }, 6489 undo: function (cm) { return cm.undo(); }, 6490 redo: function (cm) { return cm.redo(); }, 6491 undoSelection: function (cm) { return cm.undoSelection(); }, 6492 redoSelection: function (cm) { return cm.redoSelection(); }, 6493 goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, 6494 goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, 6495 goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, 6496 {origin: "+move", bias: 1} 6497 ); }, 6498 goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, 6499 {origin: "+move", bias: 1} 6500 ); }, 6501 goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, 6502 {origin: "+move", bias: -1} 6503 ); }, 6504 goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { 6505 var top = cm.charCoords(range.head, "div").top + 5 6506 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") 6507 }, sel_move); }, 6508 goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { 6509 var top = cm.charCoords(range.head, "div").top + 5 6510 return cm.coordsChar({left: 0, top: top}, "div") 6511 }, sel_move); }, 6512 goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { 6513 var top = cm.charCoords(range.head, "div").top + 5 6514 var pos = cm.coordsChar({left: 0, top: top}, "div") 6515 if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } 6516 return pos 6517 }, sel_move); }, 6518 goLineUp: function (cm) { return cm.moveV(-1, "line"); }, 6519 goLineDown: function (cm) { return cm.moveV(1, "line"); }, 6520 goPageUp: function (cm) { return cm.moveV(-1, "page"); }, 6521 goPageDown: function (cm) { return cm.moveV(1, "page"); }, 6522 goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, 6523 goCharRight: function (cm) { return cm.moveH(1, "char"); }, 6524 goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, 6525 goColumnRight: function (cm) { return cm.moveH(1, "column"); }, 6526 goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, 6527 goGroupRight: function (cm) { return cm.moveH(1, "group"); }, 6528 goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, 6529 goWordRight: function (cm) { return cm.moveH(1, "word"); }, 6530 delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, 6531 delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, 6532 delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, 6533 delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, 6534 delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, 6535 delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, 6536 indentAuto: function (cm) { return cm.indentSelection("smart"); }, 6537 indentMore: function (cm) { return cm.indentSelection("add"); }, 6538 indentLess: function (cm) { return cm.indentSelection("subtract"); }, 6539 insertTab: function (cm) { return cm.replaceSelection("\t"); }, 6540 insertSoftTab: function (cm) { 6541 var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize 6542 for (var i = 0; i < ranges.length; i++) { 6543 var pos = ranges[i].from() 6544 var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize) 6545 spaces.push(spaceStr(tabSize - col % tabSize)) 6546 } 6547 cm.replaceSelections(spaces) 6548 }, 6549 defaultTab: function (cm) { 6550 if (cm.somethingSelected()) { cm.indentSelection("add") } 6551 else { cm.execCommand("insertTab") } 6552 }, 6553 // Swap the two chars left and right of each selection's head. 6554 // Move cursor behind the two swapped characters afterwards. 6555 // 6556 // Doesn't consider line feeds a character. 6557 // Doesn't scan more than one line above to find a character. 6558 // Doesn't do anything on an empty line. 6559 // Doesn't do anything with non-empty selections. 6560 transposeChars: function (cm) { return runInOp(cm, function () { 6561 var ranges = cm.listSelections(), newSel = [] 6562 for (var i = 0; i < ranges.length; i++) { 6563 if (!ranges[i].empty()) { continue } 6564 var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text 6565 if (line) { 6566 if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1) } 6567 if (cur.ch > 0) { 6568 cur = new Pos(cur.line, cur.ch + 1) 6569 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), 6570 Pos(cur.line, cur.ch - 2), cur, "+transpose") 6571 } else if (cur.line > cm.doc.first) { 6572 var prev = getLine(cm.doc, cur.line - 1).text 6573 if (prev) { 6574 cur = new Pos(cur.line, 1) 6575 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + 6576 prev.charAt(prev.length - 1), 6577 Pos(cur.line - 1, prev.length - 1), cur, "+transpose") 6578 } 6579 } 6580 } 6581 newSel.push(new Range(cur, cur)) 6582 } 6583 cm.setSelections(newSel) 6584 }); }, 6585 newlineAndIndent: function (cm) { return runInOp(cm, function () { 6586 var sels = cm.listSelections() 6587 for (var i = sels.length - 1; i >= 0; i--) 6588 { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") } 6589 sels = cm.listSelections() 6590 for (var i$1 = 0; i$1 < sels.length; i$1++) 6591 { cm.indentLine(sels[i$1].from().line, null, true) } 6592 ensureCursorVisible(cm) 6593 }); }, 6594 openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, 6595 toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } 6596 } 6597 6598 6599 function lineStart(cm, lineN) { 6600 var line = getLine(cm.doc, lineN) 6601 var visual = visualLine(line) 6602 if (visual != line) { lineN = lineNo(visual) } 6603 var order = getOrder(visual) 6604 var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual) 6605 return Pos(lineN, ch) 6606 } 6607 function lineEnd(cm, lineN) { 6608 var merged, line = getLine(cm.doc, lineN) 6609 while (merged = collapsedSpanAtEnd(line)) { 6610 line = merged.find(1, true).line 6611 lineN = null 6612 } 6613 var order = getOrder(line) 6614 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line) 6615 return Pos(lineN == null ? lineNo(line) : lineN, ch) 6616 } 6617 function lineStartSmart(cm, pos) { 6618 var start = lineStart(cm, pos.line) 6619 var line = getLine(cm.doc, start.line) 6620 var order = getOrder(line) 6621 if (!order || order[0].level == 0) { 6622 var firstNonWS = Math.max(0, line.text.search(/\S/)) 6623 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch 6624 return Pos(start.line, inWS ? 0 : firstNonWS) 6625 } 6626 return start 6627 } 6628 6629 // Run a handler that was bound to a key. 6630 function doHandleBinding(cm, bound, dropShift) { 6631 if (typeof bound == "string") { 6632 bound = commands[bound] 6633 if (!bound) { return false } 6634 } 6635 // Ensure previous input has been read, so that the handler sees a 6636 // consistent view of the document 6637 cm.display.input.ensurePolled() 6638 var prevShift = cm.display.shift, done = false 6639 try { 6640 if (cm.isReadOnly()) { cm.state.suppressEdits = true } 6641 if (dropShift) { cm.display.shift = false } 6642 done = bound(cm) != Pass 6643 } finally { 6644 cm.display.shift = prevShift 6645 cm.state.suppressEdits = false 6646 } 6647 return done 6648 } 6649 6650 function lookupKeyForEditor(cm, name, handle) { 6651 for (var i = 0; i < cm.state.keyMaps.length; i++) { 6652 var result = lookupKey(name, cm.state.keyMaps[i], handle, cm) 6653 if (result) { return result } 6654 } 6655 return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) 6656 || lookupKey(name, cm.options.keyMap, handle, cm) 6657 } 6658 6659 var stopSeq = new Delayed 6660 function dispatchKey(cm, name, e, handle) { 6661 var seq = cm.state.keySeq 6662 if (seq) { 6663 if (isModifierKey(name)) { return "handled" } 6664 stopSeq.set(50, function () { 6665 if (cm.state.keySeq == seq) { 6666 cm.state.keySeq = null 6667 cm.display.input.reset() 6668 } 6669 }) 6670 name = seq + " " + name 6671 } 6672 var result = lookupKeyForEditor(cm, name, handle) 6673 6674 if (result == "multi") 6675 { cm.state.keySeq = name } 6676 if (result == "handled") 6677 { signalLater(cm, "keyHandled", cm, name, e) } 6678 6679 if (result == "handled" || result == "multi") { 6680 e_preventDefault(e) 6681 restartBlink(cm) 6682 } 6683 6684 if (seq && !result && /\'$/.test(name)) { 6685 e_preventDefault(e) 6686 return true 6687 } 6688 return !!result 6689 } 6690 6691 // Handle a key from the keydown event. 6692 function handleKeyBinding(cm, e) { 6693 var name = keyName(e, true) 6694 if (!name) { return false } 6695 6696 if (e.shiftKey && !cm.state.keySeq) { 6697 // First try to resolve full name (including 'Shift-'). Failing 6698 // that, see if there is a cursor-motion command (starting with 6699 // 'go') bound to the keyname without 'Shift-'. 6700 return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) 6701 || dispatchKey(cm, name, e, function (b) { 6702 if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) 6703 { return doHandleBinding(cm, b) } 6704 }) 6705 } else { 6706 return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) 6707 } 6708 } 6709 6710 // Handle a key from the keypress event 6711 function handleCharBinding(cm, e, ch) { 6712 return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) 6713 } 6714 6715 var lastStoppedKey = null 6716 function onKeyDown(e) { 6717 var cm = this 6718 cm.curOp.focus = activeElt() 6719 if (signalDOMEvent(cm, e)) { return } 6720 // IE does strange things with escape. 6721 if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false } 6722 var code = e.keyCode 6723 cm.display.shift = code == 16 || e.shiftKey 6724 var handled = handleKeyBinding(cm, e) 6725 if (presto) { 6726 lastStoppedKey = handled ? code : null 6727 // Opera has no cut event... we try to at least catch the key combo 6728 if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) 6729 { cm.replaceSelection("", null, "cut") } 6730 } 6731 6732 // Turn mouse into crosshair when Alt is held on Mac. 6733 if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) 6734 { showCrossHair(cm) } 6735 } 6736 6737 function showCrossHair(cm) { 6738 var lineDiv = cm.display.lineDiv 6739 addClass(lineDiv, "CodeMirror-crosshair") 6740 6741 function up(e) { 6742 if (e.keyCode == 18 || !e.altKey) { 6743 rmClass(lineDiv, "CodeMirror-crosshair") 6744 off(document, "keyup", up) 6745 off(document, "mouseover", up) 6746 } 6747 } 6748 on(document, "keyup", up) 6749 on(document, "mouseover", up) 6750 } 6751 6752 function onKeyUp(e) { 6753 if (e.keyCode == 16) { this.doc.sel.shift = false } 6754 signalDOMEvent(this, e) 6755 } 6756 6757 function onKeyPress(e) { 6758 var cm = this 6759 if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } 6760 var keyCode = e.keyCode, charCode = e.charCode 6761 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} 6762 if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } 6763 var ch = String.fromCharCode(charCode == null ? keyCode : charCode) 6764 // Some browsers fire keypress events for backspace 6765 if (ch == "\x08") { return } 6766 if (handleCharBinding(cm, e, ch)) { return } 6767 cm.display.input.onKeyPress(e) 6768 } 6769 6770 // A mouse down can be a single click, double click, triple click, 6771 // start of selection drag, start of text drag, new cursor 6772 // (ctrl-click), rectangle drag (alt-drag), or xwin 6773 // middle-click-paste. Or it might be a click on something we should 6774 // not interfere with, such as a scrollbar or widget. 6775 function onMouseDown(e) { 6776 var cm = this, display = cm.display 6777 if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } 6778 display.input.ensurePolled() 6779 display.shift = e.shiftKey 6780 6781 if (eventInWidget(display, e)) { 6782 if (!webkit) { 6783 // Briefly turn off draggability, to allow widgets to do 6784 // normal dragging things. 6785 display.scroller.draggable = false 6786 setTimeout(function () { return display.scroller.draggable = true; }, 100) 6787 } 6788 return 6789 } 6790 if (clickInGutter(cm, e)) { return } 6791 var start = posFromMouse(cm, e) 6792 window.focus() 6793 6794 switch (e_button(e)) { 6795 case 1: 6796 // #3261: make sure, that we're not starting a second selection 6797 if (cm.state.selectingText) 6798 { cm.state.selectingText(e) } 6799 else if (start) 6800 { leftButtonDown(cm, e, start) } 6801 else if (e_target(e) == display.scroller) 6802 { e_preventDefault(e) } 6803 break 6804 case 2: 6805 if (webkit) { cm.state.lastMiddleDown = +new Date } 6806 if (start) { extendSelection(cm.doc, start) } 6807 setTimeout(function () { return display.input.focus(); }, 20) 6808 e_preventDefault(e) 6809 break 6810 case 3: 6811 if (captureRightClick) { onContextMenu(cm, e) } 6812 else { delayBlurEvent(cm) } 6813 break 6814 } 6815 } 6816 6817 var lastClick; 6818 var lastDoubleClick; 6819 function leftButtonDown(cm, e, start) { 6820 if (ie) { setTimeout(bind(ensureFocus, cm), 0) } 6821 else { cm.curOp.focus = activeElt() } 6822 6823 var now = +new Date, type 6824 if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { 6825 type = "triple" 6826 } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { 6827 type = "double" 6828 lastDoubleClick = {time: now, pos: start} 6829 } else { 6830 type = "single" 6831 lastClick = {time: now, pos: start} 6832 } 6833 6834 var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained 6835 if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && 6836 type == "single" && (contained = sel.contains(start)) > -1 && 6837 (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) && 6838 (cmp(contained.to(), start) > 0 || start.xRel < 0)) 6839 { leftButtonStartDrag(cm, e, start, modifier) } 6840 else 6841 { leftButtonSelect(cm, e, start, type, modifier) } 6842 } 6843 6844 // Start a text drag. When it ends, see if any dragging actually 6845 // happen, and treat as a click if it didn't. 6846 function leftButtonStartDrag(cm, e, start, modifier) { 6847 var display = cm.display, startTime = +new Date 6848 var dragEnd = operation(cm, function (e2) { 6849 if (webkit) { display.scroller.draggable = false } 6850 cm.state.draggingText = false 6851 off(document, "mouseup", dragEnd) 6852 off(display.scroller, "drop", dragEnd) 6853 if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { 6854 e_preventDefault(e2) 6855 if (!modifier && +new Date - 200 < startTime) 6856 { extendSelection(cm.doc, start) } 6857 // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) 6858 if (webkit || ie && ie_version == 9) 6859 { setTimeout(function () {document.body.focus(); display.input.focus()}, 20) } 6860 else 6861 { display.input.focus() } 6862 } 6863 }) 6864 // Let the drag handler handle this. 6865 if (webkit) { display.scroller.draggable = true } 6866 cm.state.draggingText = dragEnd 6867 dragEnd.copy = mac ? e.altKey : e.ctrlKey 6868 // IE's approach to draggable 6869 if (display.scroller.dragDrop) { display.scroller.dragDrop() } 6870 on(document, "mouseup", dragEnd) 6871 on(display.scroller, "drop", dragEnd) 6872 } 6873 6874 // Normal selection, as opposed to text dragging. 6875 function leftButtonSelect(cm, e, start, type, addNew) { 6876 var display = cm.display, doc = cm.doc 6877 e_preventDefault(e) 6878 6879 var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges 6880 if (addNew && !e.shiftKey) { 6881 ourIndex = doc.sel.contains(start) 6882 if (ourIndex > -1) 6883 { ourRange = ranges[ourIndex] } 6884 else 6885 { ourRange = new Range(start, start) } 6886 } else { 6887 ourRange = doc.sel.primary() 6888 ourIndex = doc.sel.primIndex 6889 } 6890 6891 if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) { 6892 type = "rect" 6893 if (!addNew) { ourRange = new Range(start, start) } 6894 start = posFromMouse(cm, e, true, true) 6895 ourIndex = -1 6896 } else if (type == "double") { 6897 var word = cm.findWordAt(start) 6898 if (cm.display.shift || doc.extend) 6899 { ourRange = extendRange(doc, ourRange, word.anchor, word.head) } 6900 else 6901 { ourRange = word } 6902 } else if (type == "triple") { 6903 var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))) 6904 if (cm.display.shift || doc.extend) 6905 { ourRange = extendRange(doc, ourRange, line.anchor, line.head) } 6906 else 6907 { ourRange = line } 6908 } else { 6909 ourRange = extendRange(doc, ourRange, start) 6910 } 6911 6912 if (!addNew) { 6913 ourIndex = 0 6914 setSelection(doc, new Selection([ourRange], 0), sel_mouse) 6915 startSel = doc.sel 6916 } else if (ourIndex == -1) { 6917 ourIndex = ranges.length 6918 setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), 6919 {scroll: false, origin: "*mouse"}) 6920 } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) { 6921 setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), 6922 {scroll: false, origin: "*mouse"}) 6923 startSel = doc.sel 6924 } else { 6925 replaceOneSelection(doc, ourIndex, ourRange, sel_mouse) 6926 } 6927 6928 var lastPos = start 6929 function extendTo(pos) { 6930 if (cmp(lastPos, pos) == 0) { return } 6931 lastPos = pos 6932 6933 if (type == "rect") { 6934 var ranges = [], tabSize = cm.options.tabSize 6935 var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize) 6936 var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize) 6937 var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol) 6938 for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); 6939 line <= end; line++) { 6940 var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize) 6941 if (left == right) 6942 { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) } 6943 else if (text.length > leftPos) 6944 { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) } 6945 } 6946 if (!ranges.length) { ranges.push(new Range(start, start)) } 6947 setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), 6948 {origin: "*mouse", scroll: false}) 6949 cm.scrollIntoView(pos) 6950 } else { 6951 var oldRange = ourRange 6952 var anchor = oldRange.anchor, head = pos 6953 if (type != "single") { 6954 var range 6955 if (type == "double") 6956 { range = cm.findWordAt(pos) } 6957 else 6958 { range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) } 6959 if (cmp(range.anchor, anchor) > 0) { 6960 head = range.head 6961 anchor = minPos(oldRange.from(), range.anchor) 6962 } else { 6963 head = range.anchor 6964 anchor = maxPos(oldRange.to(), range.head) 6965 } 6966 } 6967 var ranges$1 = startSel.ranges.slice(0) 6968 ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head) 6969 setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse) 6970 } 6971 } 6972 6973 var editorSize = display.wrapper.getBoundingClientRect() 6974 // Used to ensure timeout re-tries don't fire when another extend 6975 // happened in the meantime (clearTimeout isn't reliable -- at 6976 // least on Chrome, the timeouts still happen even when cleared, 6977 // if the clear happens after their scheduled firing time). 6978 var counter = 0 6979 6980 function extend(e) { 6981 var curCount = ++counter 6982 var cur = posFromMouse(cm, e, true, type == "rect") 6983 if (!cur) { return } 6984 if (cmp(cur, lastPos) != 0) { 6985 cm.curOp.focus = activeElt() 6986 extendTo(cur) 6987 var visible = visibleLines(display, doc) 6988 if (cur.line >= visible.to || cur.line < visible.from) 6989 { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e) }}), 150) } 6990 } else { 6991 var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0 6992 if (outside) { setTimeout(operation(cm, function () { 6993 if (counter != curCount) { return } 6994 display.scroller.scrollTop += outside 6995 extend(e) 6996 }), 50) } 6997 } 6998 } 6999 7000 function done(e) { 7001 cm.state.selectingText = false 7002 counter = Infinity 7003 e_preventDefault(e) 7004 display.input.focus() 7005 off(document, "mousemove", move) 7006 off(document, "mouseup", up) 7007 doc.history.lastSelOrigin = null 7008 } 7009 7010 var move = operation(cm, function (e) { 7011 if (!e_button(e)) { done(e) } 7012 else { extend(e) } 7013 }) 7014 var up = operation(cm, done) 7015 cm.state.selectingText = up 7016 on(document, "mousemove", move) 7017 on(document, "mouseup", up) 7018 } 7019 7020 7021 // Determines whether an event happened in the gutter, and fires the 7022 // handlers for the corresponding event. 7023 function gutterEvent(cm, e, type, prevent) { 7024 var mX, mY 7025 try { mX = e.clientX; mY = e.clientY } 7026 catch(e) { return false } 7027 if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } 7028 if (prevent) { e_preventDefault(e) } 7029 7030 var display = cm.display 7031 var lineBox = display.lineDiv.getBoundingClientRect() 7032 7033 if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } 7034 mY -= lineBox.top - display.viewOffset 7035 7036 for (var i = 0; i < cm.options.gutters.length; ++i) { 7037 var g = display.gutters.childNodes[i] 7038 if (g && g.getBoundingClientRect().right >= mX) { 7039 var line = lineAtHeight(cm.doc, mY) 7040 var gutter = cm.options.gutters[i] 7041 signal(cm, type, cm, line, gutter, e) 7042 return e_defaultPrevented(e) 7043 } 7044 } 7045 } 7046 7047 function clickInGutter(cm, e) { 7048 return gutterEvent(cm, e, "gutterClick", true) 7049 } 7050 7051 // CONTEXT MENU HANDLING 7052 7053 // To make the context menu work, we need to briefly unhide the 7054 // textarea (making it as unobtrusive as possible) to let the 7055 // right-click take effect on it. 7056 function onContextMenu(cm, e) { 7057 if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } 7058 if (signalDOMEvent(cm, e, "contextmenu")) { return } 7059 cm.display.input.onContextMenu(e) 7060 } 7061 7062 function contextMenuInGutter(cm, e) { 7063 if (!hasHandler(cm, "gutterContextMenu")) { return false } 7064 return gutterEvent(cm, e, "gutterContextMenu", false) 7065 } 7066 7067 function themeChanged(cm) { 7068 cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + 7069 cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-") 7070 clearCaches(cm) 7071 } 7072 7073 var Init = {toString: function(){return "CodeMirror.Init"}} 7074 7075 var defaults = {} 7076 var optionHandlers = {} 7077 7078 function defineOptions(CodeMirror) { 7079 var optionHandlers = CodeMirror.optionHandlers 7080 7081 function option(name, deflt, handle, notOnInit) { 7082 CodeMirror.defaults[name] = deflt 7083 if (handle) { optionHandlers[name] = 7084 notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old) }} : handle } 7085 } 7086 7087 CodeMirror.defineOption = option 7088 7089 // Passed to option handlers when there is no old value. 7090 CodeMirror.Init = Init 7091 7092 // These two are, on init, called from the constructor because they 7093 // have to be initialized before the editor can start at all. 7094 option("value", "", function (cm, val) { return cm.setValue(val); }, true) 7095 option("mode", null, function (cm, val) { 7096 cm.doc.modeOption = val 7097 loadMode(cm) 7098 }, true) 7099 7100 option("indentUnit", 2, loadMode, true) 7101 option("indentWithTabs", false) 7102 option("smartIndent", true) 7103 option("tabSize", 4, function (cm) { 7104 resetModeState(cm) 7105 clearCaches(cm) 7106 regChange(cm) 7107 }, true) 7108 option("lineSeparator", null, function (cm, val) { 7109 cm.doc.lineSep = val 7110 if (!val) { return } 7111 var newBreaks = [], lineNo = cm.doc.first 7112 cm.doc.iter(function (line) { 7113 for (var pos = 0;;) { 7114 var found = line.text.indexOf(val, pos) 7115 if (found == -1) { break } 7116 pos = found + val.length 7117 newBreaks.push(Pos(lineNo, found)) 7118 } 7119 lineNo++ 7120 }) 7121 for (var i = newBreaks.length - 1; i >= 0; i--) 7122 { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) } 7123 }) 7124 option("specialChars", /[\u0000-\u001f\u007f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) { 7125 cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") 7126 if (old != Init) { cm.refresh() } 7127 }) 7128 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true) 7129 option("electricChars", true) 7130 option("inputStyle", mobile ? "contenteditable" : "textarea", function () { 7131 throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME 7132 }, true) 7133 option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true) 7134 option("rtlMoveVisually", !windows) 7135 option("wholeLineUpdateBefore", true) 7136 7137 option("theme", "default", function (cm) { 7138 themeChanged(cm) 7139 guttersChanged(cm) 7140 }, true) 7141 option("keyMap", "default", function (cm, val, old) { 7142 var next = getKeyMap(val) 7143 var prev = old != Init && getKeyMap(old) 7144 if (prev && prev.detach) { prev.detach(cm, next) } 7145 if (next.attach) { next.attach(cm, prev || null) } 7146 }) 7147 option("extraKeys", null) 7148 7149 option("lineWrapping", false, wrappingChanged, true) 7150 option("gutters", [], function (cm) { 7151 setGuttersForLineNumbers(cm.options) 7152 guttersChanged(cm) 7153 }, true) 7154 option("fixedGutter", true, function (cm, val) { 7155 cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0" 7156 cm.refresh() 7157 }, true) 7158 option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true) 7159 option("scrollbarStyle", "native", function (cm) { 7160 initScrollbars(cm) 7161 updateScrollbars(cm) 7162 cm.display.scrollbars.setScrollTop(cm.doc.scrollTop) 7163 cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft) 7164 }, true) 7165 option("lineNumbers", false, function (cm) { 7166 setGuttersForLineNumbers(cm.options) 7167 guttersChanged(cm) 7168 }, true) 7169 option("firstLineNumber", 1, guttersChanged, true) 7170 option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true) 7171 option("showCursorWhenSelecting", false, updateSelection, true) 7172 7173 option("resetSelectionOnContextMenu", true) 7174 option("lineWiseCopyCut", true) 7175 7176 option("readOnly", false, function (cm, val) { 7177 if (val == "nocursor") { 7178 onBlur(cm) 7179 cm.display.input.blur() 7180 cm.display.disabled = true 7181 } else { 7182 cm.display.disabled = false 7183 } 7184 cm.display.input.readOnlyChanged(val) 7185 }) 7186 option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset() }}, true) 7187 option("dragDrop", true, dragDropChanged) 7188 option("allowDropFileTypes", null) 7189 7190 option("cursorBlinkRate", 530) 7191 option("cursorScrollMargin", 0) 7192 option("cursorHeight", 1, updateSelection, true) 7193 option("singleCursorHeightPerLine", true, updateSelection, true) 7194 option("workTime", 100) 7195 option("workDelay", 100) 7196 option("flattenSpans", true, resetModeState, true) 7197 option("addModeClass", false, resetModeState, true) 7198 option("pollInterval", 100) 7199 option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }) 7200 option("historyEventDelay", 1250) 7201 option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true) 7202 option("maxHighlightLength", 10000, resetModeState, true) 7203 option("moveInputWithCursor", true, function (cm, val) { 7204 if (!val) { cm.display.input.resetPosition() } 7205 }) 7206 7207 option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }) 7208 option("autofocus", null) 7209 } 7210 7211 function guttersChanged(cm) { 7212 updateGutters(cm) 7213 regChange(cm) 7214 alignHorizontally(cm) 7215 } 7216 7217 function dragDropChanged(cm, value, old) { 7218 var wasOn = old && old != Init 7219 if (!value != !wasOn) { 7220 var funcs = cm.display.dragFunctions 7221 var toggle = value ? on : off 7222 toggle(cm.display.scroller, "dragstart", funcs.start) 7223 toggle(cm.display.scroller, "dragenter", funcs.enter) 7224 toggle(cm.display.scroller, "dragover", funcs.over) 7225 toggle(cm.display.scroller, "dragleave", funcs.leave) 7226 toggle(cm.display.scroller, "drop", funcs.drop) 7227 } 7228 } 7229 7230 function wrappingChanged(cm) { 7231 if (cm.options.lineWrapping) { 7232 addClass(cm.display.wrapper, "CodeMirror-wrap") 7233 cm.display.sizer.style.minWidth = "" 7234 cm.display.sizerWidth = null 7235 } else { 7236 rmClass(cm.display.wrapper, "CodeMirror-wrap") 7237 findMaxLine(cm) 7238 } 7239 estimateLineHeights(cm) 7240 regChange(cm) 7241 clearCaches(cm) 7242 setTimeout(function () { return updateScrollbars(cm); }, 100) 7243 } 7244 7245 // A CodeMirror instance represents an editor. This is the object 7246 // that user code is usually dealing with. 7247 7248 function CodeMirror(place, options) { 7249 var this$1 = this; 7250 7251 if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } 7252 7253 this.options = options = options ? copyObj(options) : {} 7254 // Determine effective options based on given values and defaults. 7255 copyObj(defaults, options, false) 7256 setGuttersForLineNumbers(options) 7257 7258 var doc = options.value 7259 if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator) } 7260 this.doc = doc 7261 7262 var input = new CodeMirror.inputStyles[options.inputStyle](this) 7263 var display = this.display = new Display(place, doc, input) 7264 display.wrapper.CodeMirror = this 7265 updateGutters(this) 7266 themeChanged(this) 7267 if (options.lineWrapping) 7268 { this.display.wrapper.className += " CodeMirror-wrap" } 7269 initScrollbars(this) 7270 7271 this.state = { 7272 keyMaps: [], // stores maps added by addKeyMap 7273 overlays: [], // highlighting overlays, as added by addOverlay 7274 modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info 7275 overwrite: false, 7276 delayingBlurEvent: false, 7277 focused: false, 7278 suppressEdits: false, // used to disable editing during key handlers when in readOnly mode 7279 pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll 7280 selectingText: false, 7281 draggingText: false, 7282 highlight: new Delayed(), // stores highlight worker timeout 7283 keySeq: null, // Unfinished key sequence 7284 specialChars: null 7285 } 7286 7287 if (options.autofocus && !mobile) { display.input.focus() } 7288 7289 // Override magic textarea content restore that IE sometimes does 7290 // on our hidden textarea on reload 7291 if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) } 7292 7293 registerEventHandlers(this) 7294 ensureGlobalHandlers() 7295 7296 startOperation(this) 7297 this.curOp.forceUpdate = true 7298 attachDoc(this, doc) 7299 7300 if ((options.autofocus && !mobile) || this.hasFocus()) 7301 { setTimeout(bind(onFocus, this), 20) } 7302 else 7303 { onBlur(this) } 7304 7305 for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) 7306 { optionHandlers[opt](this$1, options[opt], Init) } } 7307 maybeUpdateLineNumberWidth(this) 7308 if (options.finishInit) { options.finishInit(this) } 7309 for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1) } 7310 endOperation(this) 7311 // Suppress optimizelegibility in Webkit, since it breaks text 7312 // measuring on line wrapping boundaries. 7313 if (webkit && options.lineWrapping && 7314 getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") 7315 { display.lineDiv.style.textRendering = "auto" } 7316 } 7317 7318 // The default configuration options. 7319 CodeMirror.defaults = defaults 7320 // Functions to run when options are changed. 7321 CodeMirror.optionHandlers = optionHandlers 7322 7323 // Attach the necessary event handlers when initializing the editor 7324 function registerEventHandlers(cm) { 7325 var d = cm.display 7326 on(d.scroller, "mousedown", operation(cm, onMouseDown)) 7327 // Older IE's will not fire a second mousedown for a double click 7328 if (ie && ie_version < 11) 7329 { on(d.scroller, "dblclick", operation(cm, function (e) { 7330 if (signalDOMEvent(cm, e)) { return } 7331 var pos = posFromMouse(cm, e) 7332 if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } 7333 e_preventDefault(e) 7334 var word = cm.findWordAt(pos) 7335 extendSelection(cm.doc, word.anchor, word.head) 7336 })) } 7337 else 7338 { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }) } 7339 // Some browsers fire contextmenu *after* opening the menu, at 7340 // which point we can't mess with it anymore. Context menu is 7341 // handled in onMouseDown for these browsers. 7342 if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }) } 7343 7344 // Used to suppress mouse event handling when a touch happens 7345 var touchFinished, prevTouch = {end: 0} 7346 function finishTouch() { 7347 if (d.activeTouch) { 7348 touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000) 7349 prevTouch = d.activeTouch 7350 prevTouch.end = +new Date 7351 } 7352 } 7353 function isMouseLikeTouchEvent(e) { 7354 if (e.touches.length != 1) { return false } 7355 var touch = e.touches[0] 7356 return touch.radiusX <= 1 && touch.radiusY <= 1 7357 } 7358 function farAway(touch, other) { 7359 if (other.left == null) { return true } 7360 var dx = other.left - touch.left, dy = other.top - touch.top 7361 return dx * dx + dy * dy > 20 * 20 7362 } 7363 on(d.scroller, "touchstart", function (e) { 7364 if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { 7365 d.input.ensurePolled() 7366 clearTimeout(touchFinished) 7367 var now = +new Date 7368 d.activeTouch = {start: now, moved: false, 7369 prev: now - prevTouch.end <= 300 ? prevTouch : null} 7370 if (e.touches.length == 1) { 7371 d.activeTouch.left = e.touches[0].pageX 7372 d.activeTouch.top = e.touches[0].pageY 7373 } 7374 } 7375 }) 7376 on(d.scroller, "touchmove", function () { 7377 if (d.activeTouch) { d.activeTouch.moved = true } 7378 }) 7379 on(d.scroller, "touchend", function (e) { 7380 var touch = d.activeTouch 7381 if (touch && !eventInWidget(d, e) && touch.left != null && 7382 !touch.moved && new Date - touch.start < 300) { 7383 var pos = cm.coordsChar(d.activeTouch, "page"), range 7384 if (!touch.prev || farAway(touch, touch.prev)) // Single tap 7385 { range = new Range(pos, pos) } 7386 else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap 7387 { range = cm.findWordAt(pos) } 7388 else // Triple tap 7389 { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } 7390 cm.setSelection(range.anchor, range.head) 7391 cm.focus() 7392 e_preventDefault(e) 7393 } 7394 finishTouch() 7395 }) 7396 on(d.scroller, "touchcancel", finishTouch) 7397 7398 // Sync scrolling between fake scrollbars and real scrollable 7399 // area, ensure viewport is updated when scrolling. 7400 on(d.scroller, "scroll", function () { 7401 if (d.scroller.clientHeight) { 7402 setScrollTop(cm, d.scroller.scrollTop) 7403 setScrollLeft(cm, d.scroller.scrollLeft, true) 7404 signal(cm, "scroll", cm) 7405 } 7406 }) 7407 7408 // Listen to wheel events in order to try and update the viewport on time. 7409 on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }) 7410 on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }) 7411 7412 // Prevent wrapper from ever scrolling 7413 on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }) 7414 7415 d.dragFunctions = { 7416 enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e) }}, 7417 over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }}, 7418 start: function (e) { return onDragStart(cm, e); }, 7419 drop: operation(cm, onDrop), 7420 leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }} 7421 } 7422 7423 var inp = d.input.getField() 7424 on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }) 7425 on(inp, "keydown", operation(cm, onKeyDown)) 7426 on(inp, "keypress", operation(cm, onKeyPress)) 7427 on(inp, "focus", function (e) { return onFocus(cm, e); }) 7428 on(inp, "blur", function (e) { return onBlur(cm, e); }) 7429 } 7430 7431 var initHooks = [] 7432 CodeMirror.defineInitHook = function (f) { return initHooks.push(f); } 7433 7434 // Indent the given line. The how parameter can be "smart", 7435 // "add"/null, "subtract", or "prev". When aggressive is false 7436 // (typically set to true for forced single-line indents), empty 7437 // lines are not indented, and places where the mode returns Pass 7438 // are left alone. 7439 function indentLine(cm, n, how, aggressive) { 7440 var doc = cm.doc, state 7441 if (how == null) { how = "add" } 7442 if (how == "smart") { 7443 // Fall back to "prev" when the mode doesn't have an indentation 7444 // method. 7445 if (!doc.mode.indent) { how = "prev" } 7446 else { state = getStateBefore(cm, n) } 7447 } 7448 7449 var tabSize = cm.options.tabSize 7450 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) 7451 if (line.stateAfter) { line.stateAfter = null } 7452 var curSpaceString = line.text.match(/^\s*/)[0], indentation 7453 if (!aggressive && !/\S/.test(line.text)) { 7454 indentation = 0 7455 how = "not" 7456 } else if (how == "smart") { 7457 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text) 7458 if (indentation == Pass || indentation > 150) { 7459 if (!aggressive) { return } 7460 how = "prev" 7461 } 7462 } 7463 if (how == "prev") { 7464 if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize) } 7465 else { indentation = 0 } 7466 } else if (how == "add") { 7467 indentation = curSpace + cm.options.indentUnit 7468 } else if (how == "subtract") { 7469 indentation = curSpace - cm.options.indentUnit 7470 } else if (typeof how == "number") { 7471 indentation = curSpace + how 7472 } 7473 indentation = Math.max(0, indentation) 7474 7475 var indentString = "", pos = 0 7476 if (cm.options.indentWithTabs) 7477 { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} } 7478 if (pos < indentation) { indentString += spaceStr(indentation - pos) } 7479 7480 if (indentString != curSpaceString) { 7481 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input") 7482 line.stateAfter = null 7483 return true 7484 } else { 7485 // Ensure that, if the cursor was in the whitespace at the start 7486 // of the line, it is moved to the end of that space. 7487 for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { 7488 var range = doc.sel.ranges[i$1] 7489 if (range.head.line == n && range.head.ch < curSpaceString.length) { 7490 var pos$1 = Pos(n, curSpaceString.length) 7491 replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)) 7492 break 7493 } 7494 } 7495 } 7496 } 7497 7498 // This will be set to a {lineWise: bool, text: [string]} object, so 7499 // that, when pasting, we know what kind of selections the copied 7500 // text was made out of. 7501 var lastCopied = null 7502 7503 function setLastCopied(newLastCopied) { 7504 lastCopied = newLastCopied 7505 } 7506 7507 function applyTextInput(cm, inserted, deleted, sel, origin) { 7508 var doc = cm.doc 7509 cm.display.shift = false 7510 if (!sel) { sel = doc.sel } 7511 7512 var paste = cm.state.pasteIncoming || origin == "paste" 7513 var textLines = splitLinesAuto(inserted), multiPaste = null 7514 // When pasing N lines into N selections, insert one line per selection 7515 if (paste && sel.ranges.length > 1) { 7516 if (lastCopied && lastCopied.text.join("\n") == inserted) { 7517 if (sel.ranges.length % lastCopied.text.length == 0) { 7518 multiPaste = [] 7519 for (var i = 0; i < lastCopied.text.length; i++) 7520 { multiPaste.push(doc.splitLines(lastCopied.text[i])) } 7521 } 7522 } else if (textLines.length == sel.ranges.length) { 7523 multiPaste = map(textLines, function (l) { return [l]; }) 7524 } 7525 } 7526 7527 var updateInput 7528 // Normal behavior is to insert the new text into every selection 7529 for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { 7530 var range = sel.ranges[i$1] 7531 var from = range.from(), to = range.to() 7532 if (range.empty()) { 7533 if (deleted && deleted > 0) // Handle deletion 7534 { from = Pos(from.line, from.ch - deleted) } 7535 else if (cm.state.overwrite && !paste) // Handle overwrite 7536 { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) } 7537 else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) 7538 { from = to = Pos(from.line, 0) } 7539 } 7540 updateInput = cm.curOp.updateInput 7541 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, 7542 origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")} 7543 makeChange(cm.doc, changeEvent) 7544 signalLater(cm, "inputRead", cm, changeEvent) 7545 } 7546 if (inserted && !paste) 7547 { triggerElectric(cm, inserted) } 7548 7549 ensureCursorVisible(cm) 7550 cm.curOp.updateInput = updateInput 7551 cm.curOp.typing = true 7552 cm.state.pasteIncoming = cm.state.cutIncoming = false 7553 } 7554 7555 function handlePaste(e, cm) { 7556 var pasted = e.clipboardData && e.clipboardData.getData("Text") 7557 if (pasted) { 7558 e.preventDefault() 7559 if (!cm.isReadOnly() && !cm.options.disableInput) 7560 { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }) } 7561 return true 7562 } 7563 } 7564 7565 function triggerElectric(cm, inserted) { 7566 // When an 'electric' character is inserted, immediately trigger a reindent 7567 if (!cm.options.electricChars || !cm.options.smartIndent) { return } 7568 var sel = cm.doc.sel 7569 7570 for (var i = sel.ranges.length - 1; i >= 0; i--) { 7571 var range = sel.ranges[i] 7572 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } 7573 var mode = cm.getModeAt(range.head) 7574 var indented = false 7575 if (mode.electricChars) { 7576 for (var j = 0; j < mode.electricChars.length; j++) 7577 { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { 7578 indented = indentLine(cm, range.head.line, "smart") 7579 break 7580 } } 7581 } else if (mode.electricInput) { 7582 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) 7583 { indented = indentLine(cm, range.head.line, "smart") } 7584 } 7585 if (indented) { signalLater(cm, "electricInput", cm, range.head.line) } 7586 } 7587 } 7588 7589 function copyableRanges(cm) { 7590 var text = [], ranges = [] 7591 for (var i = 0; i < cm.doc.sel.ranges.length; i++) { 7592 var line = cm.doc.sel.ranges[i].head.line 7593 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)} 7594 ranges.push(lineRange) 7595 text.push(cm.getRange(lineRange.anchor, lineRange.head)) 7596 } 7597 return {text: text, ranges: ranges} 7598 } 7599 7600 function disableBrowserMagic(field, spellcheck) { 7601 field.setAttribute("autocorrect", "off") 7602 field.setAttribute("autocapitalize", "off") 7603 field.setAttribute("spellcheck", !!spellcheck) 7604 } 7605 7606 function hiddenTextarea() { 7607 var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none") 7608 var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;") 7609 // The textarea is kept positioned near the cursor to prevent the 7610 // fact that it'll be scrolled into view on input from scrolling 7611 // our fake cursor out of view. On webkit, when wrap=off, paste is 7612 // very slow. So make the area wide instead. 7613 if (webkit) { te.style.width = "1000px" } 7614 else { te.setAttribute("wrap", "off") } 7615 // If border: 0; -- iOS fails to open keyboard (issue #1287) 7616 if (ios) { te.style.border = "1px solid black" } 7617 disableBrowserMagic(te) 7618 return div 7619 } 7620 7621 // The publicly visible API. Note that methodOp(f) means 7622 // 'wrap f in an operation, performed on its `this` parameter'. 7623 7624 // This is not the complete set of editor methods. Most of the 7625 // methods defined on the Doc type are also injected into 7626 // CodeMirror.prototype, for backwards compatibility and 7627 // convenience. 7628 7629 function addEditorMethods(CodeMirror) { 7630 var optionHandlers = CodeMirror.optionHandlers 7631 7632 var helpers = CodeMirror.helpers = {} 7633 7634 CodeMirror.prototype = { 7635 constructor: CodeMirror, 7636 focus: function(){window.focus(); this.display.input.focus()}, 7637 7638 setOption: function(option, value) { 7639 var options = this.options, old = options[option] 7640 if (options[option] == value && option != "mode") { return } 7641 options[option] = value 7642 if (optionHandlers.hasOwnProperty(option)) 7643 { operation(this, optionHandlers[option])(this, value, old) } 7644 signal(this, "optionChange", this, option) 7645 }, 7646 7647 getOption: function(option) {return this.options[option]}, 7648 getDoc: function() {return this.doc}, 7649 7650 addKeyMap: function(map, bottom) { 7651 this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)) 7652 }, 7653 removeKeyMap: function(map) { 7654 var maps = this.state.keyMaps 7655 for (var i = 0; i < maps.length; ++i) 7656 { if (maps[i] == map || maps[i].name == map) { 7657 maps.splice(i, 1) 7658 return true 7659 } } 7660 }, 7661 7662 addOverlay: methodOp(function(spec, options) { 7663 var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec) 7664 if (mode.startState) { throw new Error("Overlays may not be stateful.") } 7665 insertSorted(this.state.overlays, 7666 {mode: mode, modeSpec: spec, opaque: options && options.opaque, 7667 priority: (options && options.priority) || 0}, 7668 function (overlay) { return overlay.priority; }) 7669 this.state.modeGen++ 7670 regChange(this) 7671 }), 7672 removeOverlay: methodOp(function(spec) { 7673 var this$1 = this; 7674 7675 var overlays = this.state.overlays 7676 for (var i = 0; i < overlays.length; ++i) { 7677 var cur = overlays[i].modeSpec 7678 if (cur == spec || typeof spec == "string" && cur.name == spec) { 7679 overlays.splice(i, 1) 7680 this$1.state.modeGen++ 7681 regChange(this$1) 7682 return 7683 } 7684 } 7685 }), 7686 7687 indentLine: methodOp(function(n, dir, aggressive) { 7688 if (typeof dir != "string" && typeof dir != "number") { 7689 if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev" } 7690 else { dir = dir ? "add" : "subtract" } 7691 } 7692 if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive) } 7693 }), 7694 indentSelection: methodOp(function(how) { 7695 var this$1 = this; 7696 7697 var ranges = this.doc.sel.ranges, end = -1 7698 for (var i = 0; i < ranges.length; i++) { 7699 var range = ranges[i] 7700 if (!range.empty()) { 7701 var from = range.from(), to = range.to() 7702 var start = Math.max(end, from.line) 7703 end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1 7704 for (var j = start; j < end; ++j) 7705 { indentLine(this$1, j, how) } 7706 var newRanges = this$1.doc.sel.ranges 7707 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) 7708 { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) } 7709 } else if (range.head.line > end) { 7710 indentLine(this$1, range.head.line, how, true) 7711 end = range.head.line 7712 if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1) } 7713 } 7714 } 7715 }), 7716 7717 // Fetch the parser token for a given character. Useful for hacks 7718 // that want to inspect the mode state (say, for completion). 7719 getTokenAt: function(pos, precise) { 7720 return takeToken(this, pos, precise) 7721 }, 7722 7723 getLineTokens: function(line, precise) { 7724 return takeToken(this, Pos(line), precise, true) 7725 }, 7726 7727 getTokenTypeAt: function(pos) { 7728 pos = clipPos(this.doc, pos) 7729 var styles = getLineStyles(this, getLine(this.doc, pos.line)) 7730 var before = 0, after = (styles.length - 1) / 2, ch = pos.ch 7731 var type 7732 if (ch == 0) { type = styles[2] } 7733 else { for (;;) { 7734 var mid = (before + after) >> 1 7735 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid } 7736 else if (styles[mid * 2 + 1] < ch) { before = mid + 1 } 7737 else { type = styles[mid * 2 + 2]; break } 7738 } } 7739 var cut = type ? type.indexOf("overlay ") : -1 7740 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) 7741 }, 7742 7743 getModeAt: function(pos) { 7744 var mode = this.doc.mode 7745 if (!mode.innerMode) { return mode } 7746 return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode 7747 }, 7748 7749 getHelper: function(pos, type) { 7750 return this.getHelpers(pos, type)[0] 7751 }, 7752 7753 getHelpers: function(pos, type) { 7754 var this$1 = this; 7755 7756 var found = [] 7757 if (!helpers.hasOwnProperty(type)) { return found } 7758 var help = helpers[type], mode = this.getModeAt(pos) 7759 if (typeof mode[type] == "string") { 7760 if (help[mode[type]]) { found.push(help[mode[type]]) } 7761 } else if (mode[type]) { 7762 for (var i = 0; i < mode[type].length; i++) { 7763 var val = help[mode[type][i]] 7764 if (val) { found.push(val) } 7765 } 7766 } else if (mode.helperType && help[mode.helperType]) { 7767 found.push(help[mode.helperType]) 7768 } else if (help[mode.name]) { 7769 found.push(help[mode.name]) 7770 } 7771 for (var i$1 = 0; i$1 < help._global.length; i$1++) { 7772 var cur = help._global[i$1] 7773 if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) 7774 { found.push(cur.val) } 7775 } 7776 return found 7777 }, 7778 7779 getStateAfter: function(line, precise) { 7780 var doc = this.doc 7781 line = clipLine(doc, line == null ? doc.first + doc.size - 1: line) 7782 return getStateBefore(this, line + 1, precise) 7783 }, 7784 7785 cursorCoords: function(start, mode) { 7786 var pos, range = this.doc.sel.primary() 7787 if (start == null) { pos = range.head } 7788 else if (typeof start == "object") { pos = clipPos(this.doc, start) } 7789 else { pos = start ? range.from() : range.to() } 7790 return cursorCoords(this, pos, mode || "page") 7791 }, 7792 7793 charCoords: function(pos, mode) { 7794 return charCoords(this, clipPos(this.doc, pos), mode || "page") 7795 }, 7796 7797 coordsChar: function(coords, mode) { 7798 coords = fromCoordSystem(this, coords, mode || "page") 7799 return coordsChar(this, coords.left, coords.top) 7800 }, 7801 7802 lineAtHeight: function(height, mode) { 7803 height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top 7804 return lineAtHeight(this.doc, height + this.display.viewOffset) 7805 }, 7806 heightAtLine: function(line, mode, includeWidgets) { 7807 var end = false, lineObj 7808 if (typeof line == "number") { 7809 var last = this.doc.first + this.doc.size - 1 7810 if (line < this.doc.first) { line = this.doc.first } 7811 else if (line > last) { line = last; end = true } 7812 lineObj = getLine(this.doc, line) 7813 } else { 7814 lineObj = line 7815 } 7816 return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets).top + 7817 (end ? this.doc.height - heightAtLine(lineObj) : 0) 7818 }, 7819 7820 defaultTextHeight: function() { return textHeight(this.display) }, 7821 defaultCharWidth: function() { return charWidth(this.display) }, 7822 7823 getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, 7824 7825 addWidget: function(pos, node, scroll, vert, horiz) { 7826 var display = this.display 7827 pos = cursorCoords(this, clipPos(this.doc, pos)) 7828 var top = pos.bottom, left = pos.left 7829 node.style.position = "absolute" 7830 node.setAttribute("cm-ignore-events", "true") 7831 this.display.input.setUneditable(node) 7832 display.sizer.appendChild(node) 7833 if (vert == "over") { 7834 top = pos.top 7835 } else if (vert == "above" || vert == "near") { 7836 var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), 7837 hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth) 7838 // Default to positioning above (if specified and possible); otherwise default to positioning below 7839 if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) 7840 { top = pos.top - node.offsetHeight } 7841 else if (pos.bottom + node.offsetHeight <= vspace) 7842 { top = pos.bottom } 7843 if (left + node.offsetWidth > hspace) 7844 { left = hspace - node.offsetWidth } 7845 } 7846 node.style.top = top + "px" 7847 node.style.left = node.style.right = "" 7848 if (horiz == "right") { 7849 left = display.sizer.clientWidth - node.offsetWidth 7850 node.style.right = "0px" 7851 } else { 7852 if (horiz == "left") { left = 0 } 7853 else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2 } 7854 node.style.left = left + "px" 7855 } 7856 if (scroll) 7857 { scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight) } 7858 }, 7859 7860 triggerOnKeyDown: methodOp(onKeyDown), 7861 triggerOnKeyPress: methodOp(onKeyPress), 7862 triggerOnKeyUp: onKeyUp, 7863 7864 execCommand: function(cmd) { 7865 if (commands.hasOwnProperty(cmd)) 7866 { return commands[cmd].call(null, this) } 7867 }, 7868 7869 triggerElectric: methodOp(function(text) { triggerElectric(this, text) }), 7870 7871 findPosH: function(from, amount, unit, visually) { 7872 var this$1 = this; 7873 7874 var dir = 1 7875 if (amount < 0) { dir = -1; amount = -amount } 7876 var cur = clipPos(this.doc, from) 7877 for (var i = 0; i < amount; ++i) { 7878 cur = findPosH(this$1.doc, cur, dir, unit, visually) 7879 if (cur.hitSide) { break } 7880 } 7881 return cur 7882 }, 7883 7884 moveH: methodOp(function(dir, unit) { 7885 var this$1 = this; 7886 7887 this.extendSelectionsBy(function (range) { 7888 if (this$1.display.shift || this$1.doc.extend || range.empty()) 7889 { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } 7890 else 7891 { return dir < 0 ? range.from() : range.to() } 7892 }, sel_move) 7893 }), 7894 7895 deleteH: methodOp(function(dir, unit) { 7896 var sel = this.doc.sel, doc = this.doc 7897 if (sel.somethingSelected()) 7898 { doc.replaceSelection("", null, "+delete") } 7899 else 7900 { deleteNearSelection(this, function (range) { 7901 var other = findPosH(doc, range.head, dir, unit, false) 7902 return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} 7903 }) } 7904 }), 7905 7906 findPosV: function(from, amount, unit, goalColumn) { 7907 var this$1 = this; 7908 7909 var dir = 1, x = goalColumn 7910 if (amount < 0) { dir = -1; amount = -amount } 7911 var cur = clipPos(this.doc, from) 7912 for (var i = 0; i < amount; ++i) { 7913 var coords = cursorCoords(this$1, cur, "div") 7914 if (x == null) { x = coords.left } 7915 else { coords.left = x } 7916 cur = findPosV(this$1, coords, dir, unit) 7917 if (cur.hitSide) { break } 7918 } 7919 return cur 7920 }, 7921 7922 moveV: methodOp(function(dir, unit) { 7923 var this$1 = this; 7924 7925 var doc = this.doc, goals = [] 7926 var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected() 7927 doc.extendSelectionsBy(function (range) { 7928 if (collapse) 7929 { return dir < 0 ? range.from() : range.to() } 7930 var headPos = cursorCoords(this$1, range.head, "div") 7931 if (range.goalColumn != null) { headPos.left = range.goalColumn } 7932 goals.push(headPos.left) 7933 var pos = findPosV(this$1, headPos, dir, unit) 7934 if (unit == "page" && range == doc.sel.primary()) 7935 { addToScrollPos(this$1, null, charCoords(this$1, pos, "div").top - headPos.top) } 7936 return pos 7937 }, sel_move) 7938 if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) 7939 { doc.sel.ranges[i].goalColumn = goals[i] } } 7940 }), 7941 7942 // Find the word at the given position (as returned by coordsChar). 7943 findWordAt: function(pos) { 7944 var doc = this.doc, line = getLine(doc, pos.line).text 7945 var start = pos.ch, end = pos.ch 7946 if (line) { 7947 var helper = this.getHelper(pos, "wordChars") 7948 if ((pos.xRel < 0 || end == line.length) && start) { --start; } else { ++end } 7949 var startChar = line.charAt(start) 7950 var check = isWordChar(startChar, helper) 7951 ? function (ch) { return isWordChar(ch, helper); } 7952 : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } 7953 : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); } 7954 while (start > 0 && check(line.charAt(start - 1))) { --start } 7955 while (end < line.length && check(line.charAt(end))) { ++end } 7956 } 7957 return new Range(Pos(pos.line, start), Pos(pos.line, end)) 7958 }, 7959 7960 toggleOverwrite: function(value) { 7961 if (value != null && value == this.state.overwrite) { return } 7962 if (this.state.overwrite = !this.state.overwrite) 7963 { addClass(this.display.cursorDiv, "CodeMirror-overwrite") } 7964 else 7965 { rmClass(this.display.cursorDiv, "CodeMirror-overwrite") } 7966 7967 signal(this, "overwriteToggle", this, this.state.overwrite) 7968 }, 7969 hasFocus: function() { return this.display.input.getField() == activeElt() }, 7970 isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, 7971 7972 scrollTo: methodOp(function(x, y) { 7973 if (x != null || y != null) { resolveScrollToPos(this) } 7974 if (x != null) { this.curOp.scrollLeft = x } 7975 if (y != null) { this.curOp.scrollTop = y } 7976 }), 7977 getScrollInfo: function() { 7978 var scroller = this.display.scroller 7979 return {left: scroller.scrollLeft, top: scroller.scrollTop, 7980 height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, 7981 width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, 7982 clientHeight: displayHeight(this), clientWidth: displayWidth(this)} 7983 }, 7984 7985 scrollIntoView: methodOp(function(range, margin) { 7986 if (range == null) { 7987 range = {from: this.doc.sel.primary().head, to: null} 7988 if (margin == null) { margin = this.options.cursorScrollMargin } 7989 } else if (typeof range == "number") { 7990 range = {from: Pos(range, 0), to: null} 7991 } else if (range.from == null) { 7992 range = {from: range, to: null} 7993 } 7994 if (!range.to) { range.to = range.from } 7995 range.margin = margin || 0 7996 7997 if (range.from.line != null) { 7998 resolveScrollToPos(this) 7999 this.curOp.scrollToPos = range 8000 } else { 8001 var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left), 8002 Math.min(range.from.top, range.to.top) - range.margin, 8003 Math.max(range.from.right, range.to.right), 8004 Math.max(range.from.bottom, range.to.bottom) + range.margin) 8005 this.scrollTo(sPos.scrollLeft, sPos.scrollTop) 8006 } 8007 }), 8008 8009 setSize: methodOp(function(width, height) { 8010 var this$1 = this; 8011 8012 var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; } 8013 if (width != null) { this.display.wrapper.style.width = interpret(width) } 8014 if (height != null) { this.display.wrapper.style.height = interpret(height) } 8015 if (this.options.lineWrapping) { clearLineMeasurementCache(this) } 8016 var lineNo = this.display.viewFrom 8017 this.doc.iter(lineNo, this.display.viewTo, function (line) { 8018 if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) 8019 { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } 8020 ++lineNo 8021 }) 8022 this.curOp.forceUpdate = true 8023 signal(this, "refresh", this) 8024 }), 8025 8026 operation: function(f){return runInOp(this, f)}, 8027 8028 refresh: methodOp(function() { 8029 var oldHeight = this.display.cachedTextHeight 8030 regChange(this) 8031 this.curOp.forceUpdate = true 8032 clearCaches(this) 8033 this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop) 8034 updateGutterSpace(this) 8035 if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) 8036 { estimateLineHeights(this) } 8037 signal(this, "refresh", this) 8038 }), 8039 8040 swapDoc: methodOp(function(doc) { 8041 var old = this.doc 8042 old.cm = null 8043 attachDoc(this, doc) 8044 clearCaches(this) 8045 this.display.input.reset() 8046 this.scrollTo(doc.scrollLeft, doc.scrollTop) 8047 this.curOp.forceScroll = true 8048 signalLater(this, "swapDoc", this, old) 8049 return old 8050 }), 8051 8052 getInputField: function(){return this.display.input.getField()}, 8053 getWrapperElement: function(){return this.display.wrapper}, 8054 getScrollerElement: function(){return this.display.scroller}, 8055 getGutterElement: function(){return this.display.gutters} 8056 } 8057 eventMixin(CodeMirror) 8058 8059 CodeMirror.registerHelper = function(type, name, value) { 8060 if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []} } 8061 helpers[type][name] = value 8062 } 8063 CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { 8064 CodeMirror.registerHelper(type, name, value) 8065 helpers[type]._global.push({pred: predicate, val: value}) 8066 } 8067 } 8068 8069 // Used for horizontal relative motion. Dir is -1 or 1 (left or 8070 // right), unit can be "char", "column" (like char, but doesn't 8071 // cross line boundaries), "word" (across next word), or "group" (to 8072 // the start of next group of word or non-word-non-whitespace 8073 // chars). The visually param controls whether, in right-to-left 8074 // text, direction 1 means to move towards the next index in the 8075 // string, or towards the character to the right of the current 8076 // position. The resulting position will have a hitSide=true 8077 // property if it reached the end of the document. 8078 function findPosH(doc, pos, dir, unit, visually) { 8079 var line = pos.line, ch = pos.ch, origDir = dir 8080 var lineObj = getLine(doc, line) 8081 function findNextLine() { 8082 var l = line + dir 8083 if (l < doc.first || l >= doc.first + doc.size) { return false } 8084 line = l 8085 return lineObj = getLine(doc, l) 8086 } 8087 function moveOnce(boundToLine) { 8088 var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true) 8089 if (next == null) { 8090 if (!boundToLine && findNextLine()) { 8091 if (visually) { ch = (dir < 0 ? lineRight : lineLeft)(lineObj) } 8092 else { ch = dir < 0 ? lineObj.text.length : 0 } 8093 } else { return false } 8094 } else { ch = next } 8095 return true 8096 } 8097 8098 if (unit == "char") { 8099 moveOnce() 8100 } else if (unit == "column") { 8101 moveOnce(true) 8102 } else if (unit == "word" || unit == "group") { 8103 var sawType = null, group = unit == "group" 8104 var helper = doc.cm && doc.cm.getHelper(pos, "wordChars") 8105 for (var first = true;; first = false) { 8106 if (dir < 0 && !moveOnce(!first)) { break } 8107 var cur = lineObj.text.charAt(ch) || "\n" 8108 var type = isWordChar(cur, helper) ? "w" 8109 : group && cur == "\n" ? "n" 8110 : !group || /\s/.test(cur) ? null 8111 : "p" 8112 if (group && !first && !type) { type = "s" } 8113 if (sawType && sawType != type) { 8114 if (dir < 0) {dir = 1; moveOnce()} 8115 break 8116 } 8117 8118 if (type) { sawType = type } 8119 if (dir > 0 && !moveOnce(!first)) { break } 8120 } 8121 } 8122 var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true) 8123 if (!cmp(pos, result)) { result.hitSide = true } 8124 return result 8125 } 8126 8127 // For relative vertical movement. Dir may be -1 or 1. Unit can be 8128 // "page" or "line". The resulting position will have a hitSide=true 8129 // property if it reached the end of the document. 8130 function findPosV(cm, pos, dir, unit) { 8131 var doc = cm.doc, x = pos.left, y 8132 if (unit == "page") { 8133 var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight) 8134 var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3) 8135 y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount 8136 8137 } else if (unit == "line") { 8138 y = dir > 0 ? pos.bottom + 3 : pos.top - 3 8139 } 8140 var target 8141 for (;;) { 8142 target = coordsChar(cm, x, y) 8143 if (!target.outside) { break } 8144 if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } 8145 y += dir * 5 8146 } 8147 return target 8148 } 8149 8150 // CONTENTEDITABLE INPUT STYLE 8151 8152 var ContentEditableInput = function(cm) { 8153 this.cm = cm 8154 this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null 8155 this.polling = new Delayed() 8156 this.composing = null 8157 this.gracePeriod = false 8158 this.readDOMTimeout = null 8159 }; 8160 8161 ContentEditableInput.prototype.init = function (display) { 8162 var this$1 = this; 8163 8164 var input = this, cm = input.cm 8165 var div = input.div = display.lineDiv 8166 disableBrowserMagic(div, cm.options.spellcheck) 8167 8168 on(div, "paste", function (e) { 8169 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } 8170 // IE doesn't fire input events, so we schedule a read for the pasted content in this way 8171 if (ie_version <= 11) { setTimeout(operation(cm, function () { 8172 if (!input.pollContent()) { regChange(cm) } 8173 }), 20) } 8174 }) 8175 8176 on(div, "compositionstart", function (e) { 8177 this$1.composing = {data: e.data, done: false} 8178 }) 8179 on(div, "compositionupdate", function (e) { 8180 if (!this$1.composing) { this$1.composing = {data: e.data, done: false} } 8181 }) 8182 on(div, "compositionend", function (e) { 8183 if (this$1.composing) { 8184 if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() } 8185 this$1.composing.done = true 8186 } 8187 }) 8188 8189 on(div, "touchstart", function () { return input.forceCompositionEnd(); }) 8190 8191 on(div, "input", function () { 8192 if (!this$1.composing) { this$1.readFromDOMSoon() } 8193 }) 8194 8195 function onCopyCut(e) { 8196 if (signalDOMEvent(cm, e)) { return } 8197 if (cm.somethingSelected()) { 8198 setLastCopied({lineWise: false, text: cm.getSelections()}) 8199 if (e.type == "cut") { cm.replaceSelection("", null, "cut") } 8200 } else if (!cm.options.lineWiseCopyCut) { 8201 return 8202 } else { 8203 var ranges = copyableRanges(cm) 8204 setLastCopied({lineWise: true, text: ranges.text}) 8205 if (e.type == "cut") { 8206 cm.operation(function () { 8207 cm.setSelections(ranges.ranges, 0, sel_dontScroll) 8208 cm.replaceSelection("", null, "cut") 8209 }) 8210 } 8211 } 8212 if (e.clipboardData) { 8213 e.clipboardData.clearData() 8214 var content = lastCopied.text.join("\n") 8215 // iOS exposes the clipboard API, but seems to discard content inserted into it 8216 e.clipboardData.setData("Text", content) 8217 if (e.clipboardData.getData("Text") == content) { 8218 e.preventDefault() 8219 return 8220 } 8221 } 8222 // Old-fashioned briefly-focus-a-textarea hack 8223 var kludge = hiddenTextarea(), te = kludge.firstChild 8224 cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild) 8225 te.value = lastCopied.text.join("\n") 8226 var hadFocus = document.activeElement 8227 selectInput(te) 8228 setTimeout(function () { 8229 cm.display.lineSpace.removeChild(kludge) 8230 hadFocus.focus() 8231 if (hadFocus == div) { input.showPrimarySelection() } 8232 }, 50) 8233 } 8234 on(div, "copy", onCopyCut) 8235 on(div, "cut", onCopyCut) 8236 }; 8237 8238 ContentEditableInput.prototype.prepareSelection = function () { 8239 var result = prepareSelection(this.cm, false) 8240 result.focus = this.cm.state.focused 8241 return result 8242 }; 8243 8244 ContentEditableInput.prototype.showSelection = function (info, takeFocus) { 8245 if (!info || !this.cm.display.view.length) { return } 8246 if (info.focus || takeFocus) { this.showPrimarySelection() } 8247 this.showMultipleSelections(info) 8248 }; 8249 8250 ContentEditableInput.prototype.showPrimarySelection = function () { 8251 var sel = window.getSelection(), prim = this.cm.doc.sel.primary() 8252 var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset) 8253 var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset) 8254 if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && 8255 cmp(minPos(curAnchor, curFocus), prim.from()) == 0 && 8256 cmp(maxPos(curAnchor, curFocus), prim.to()) == 0) 8257 { return } 8258 8259 var start = posToDOM(this.cm, prim.from()) 8260 var end = posToDOM(this.cm, prim.to()) 8261 if (!start && !end) { return } 8262 8263 var view = this.cm.display.view 8264 var old = sel.rangeCount && sel.getRangeAt(0) 8265 if (!start) { 8266 start = {node: view[0].measure.map[2], offset: 0} 8267 } else if (!end) { // FIXME dangerously hacky 8268 var measure = view[view.length - 1].measure 8269 var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map 8270 end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]} 8271 } 8272 8273 var rng 8274 try { rng = range(start.node, start.offset, end.offset, end.node) } 8275 catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible 8276 if (rng) { 8277 if (!gecko && this.cm.state.focused) { 8278 sel.collapse(start.node, start.offset) 8279 if (!rng.collapsed) { 8280 sel.removeAllRanges() 8281 sel.addRange(rng) 8282 } 8283 } else { 8284 sel.removeAllRanges() 8285 sel.addRange(rng) 8286 } 8287 if (old && sel.anchorNode == null) { sel.addRange(old) } 8288 else if (gecko) { this.startGracePeriod() } 8289 } 8290 this.rememberSelection() 8291 }; 8292 8293 ContentEditableInput.prototype.startGracePeriod = function () { 8294 var this$1 = this; 8295 8296 clearTimeout(this.gracePeriod) 8297 this.gracePeriod = setTimeout(function () { 8298 this$1.gracePeriod = false 8299 if (this$1.selectionChanged()) 8300 { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) } 8301 }, 20) 8302 }; 8303 8304 ContentEditableInput.prototype.showMultipleSelections = function (info) { 8305 removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors) 8306 removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection) 8307 }; 8308 8309 ContentEditableInput.prototype.rememberSelection = function () { 8310 var sel = window.getSelection() 8311 this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset 8312 this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset 8313 }; 8314 8315 ContentEditableInput.prototype.selectionInEditor = function () { 8316 var sel = window.getSelection() 8317 if (!sel.rangeCount) { return false } 8318 var node = sel.getRangeAt(0).commonAncestorContainer 8319 return contains(this.div, node) 8320 }; 8321 8322 ContentEditableInput.prototype.focus = function () { 8323 if (this.cm.options.readOnly != "nocursor") { 8324 if (!this.selectionInEditor()) 8325 { this.showSelection(this.prepareSelection(), true) } 8326 this.div.focus() 8327 } 8328 }; 8329 ContentEditableInput.prototype.blur = function () { this.div.blur() }; 8330 ContentEditableInput.prototype.getField = function () { return this.div }; 8331 8332 ContentEditableInput.prototype.supportsTouch = function () { return true }; 8333 8334 ContentEditableInput.prototype.receivedFocus = function () { 8335 var input = this 8336 if (this.selectionInEditor()) 8337 { this.pollSelection() } 8338 else 8339 { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) } 8340 8341 function poll() { 8342 if (input.cm.state.focused) { 8343 input.pollSelection() 8344 input.polling.set(input.cm.options.pollInterval, poll) 8345 } 8346 } 8347 this.polling.set(this.cm.options.pollInterval, poll) 8348 }; 8349 8350 ContentEditableInput.prototype.selectionChanged = function () { 8351 var sel = window.getSelection() 8352 return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || 8353 sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset 8354 }; 8355 8356 ContentEditableInput.prototype.pollSelection = function () { 8357 if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) { 8358 var sel = window.getSelection(), cm = this.cm 8359 this.rememberSelection() 8360 var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) 8361 var head = domToPos(cm, sel.focusNode, sel.focusOffset) 8362 if (anchor && head) { runInOp(cm, function () { 8363 setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll) 8364 if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true } 8365 }) } 8366 } 8367 }; 8368 8369 ContentEditableInput.prototype.pollContent = function () { 8370 if (this.readDOMTimeout != null) { 8371 clearTimeout(this.readDOMTimeout) 8372 this.readDOMTimeout = null 8373 } 8374 8375 var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() 8376 var from = sel.from(), to = sel.to() 8377 if (from.ch == 0 && from.line > cm.firstLine()) 8378 { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) } 8379 if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) 8380 { to = Pos(to.line + 1, 0) } 8381 if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } 8382 8383 var fromIndex, fromLine, fromNode 8384 if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { 8385 fromLine = lineNo(display.view[0].line) 8386 fromNode = display.view[0].node 8387 } else { 8388 fromLine = lineNo(display.view[fromIndex].line) 8389 fromNode = display.view[fromIndex - 1].node.nextSibling 8390 } 8391 var toIndex = findViewIndex(cm, to.line) 8392 var toLine, toNode 8393 if (toIndex == display.view.length - 1) { 8394 toLine = display.viewTo - 1 8395 toNode = display.lineDiv.lastChild 8396 } else { 8397 toLine = lineNo(display.view[toIndex + 1].line) - 1 8398 toNode = display.view[toIndex + 1].node.previousSibling 8399 } 8400 8401 if (!fromNode) { return false } 8402 var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)) 8403 var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)) 8404 while (newText.length > 1 && oldText.length > 1) { 8405 if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- } 8406 else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ } 8407 else { break } 8408 } 8409 8410 var cutFront = 0, cutEnd = 0 8411 var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length) 8412 while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) 8413 { ++cutFront } 8414 var newBot = lst(newText), oldBot = lst(oldText) 8415 var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), 8416 oldBot.length - (oldText.length == 1 ? cutFront : 0)) 8417 while (cutEnd < maxCutEnd && 8418 newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) 8419 { ++cutEnd } 8420 8421 newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "") 8422 newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "") 8423 8424 var chFrom = Pos(fromLine, cutFront) 8425 var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0) 8426 if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { 8427 replaceRange(cm.doc, newText, chFrom, chTo, "+input") 8428 return true 8429 } 8430 }; 8431 8432 ContentEditableInput.prototype.ensurePolled = function () { 8433 this.forceCompositionEnd() 8434 }; 8435 ContentEditableInput.prototype.reset = function () { 8436 this.forceCompositionEnd() 8437 }; 8438 ContentEditableInput.prototype.forceCompositionEnd = function () { 8439 if (!this.composing) { return } 8440 clearTimeout(this.readDOMTimeout) 8441 this.composing = null 8442 if (!this.pollContent()) { regChange(this.cm) } 8443 this.div.blur() 8444 this.div.focus() 8445 }; 8446 ContentEditableInput.prototype.readFromDOMSoon = function () { 8447 var this$1 = this; 8448 8449 if (this.readDOMTimeout != null) { return } 8450 this.readDOMTimeout = setTimeout(function () { 8451 this$1.readDOMTimeout = null 8452 if (this$1.composing) { 8453 if (this$1.composing.done) { this$1.composing = null } 8454 else { return } 8455 } 8456 if (this$1.cm.isReadOnly() || !this$1.pollContent()) 8457 { runInOp(this$1.cm, function () { return regChange(this$1.cm); }) } 8458 }, 80) 8459 }; 8460 8461 ContentEditableInput.prototype.setUneditable = function (node) { 8462 node.contentEditable = "false" 8463 }; 8464 8465 ContentEditableInput.prototype.onKeyPress = function (e) { 8466 e.preventDefault() 8467 if (!this.cm.isReadOnly()) 8468 { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) } 8469 }; 8470 8471 ContentEditableInput.prototype.readOnlyChanged = function (val) { 8472 this.div.contentEditable = String(val != "nocursor") 8473 }; 8474 8475 ContentEditableInput.prototype.onContextMenu = function () {}; 8476 ContentEditableInput.prototype.resetPosition = function () {}; 8477 8478 ContentEditableInput.prototype.needsContentAttribute = true 8479 8480 function posToDOM(cm, pos) { 8481 var view = findViewForLine(cm, pos.line) 8482 if (!view || view.hidden) { return null } 8483 var line = getLine(cm.doc, pos.line) 8484 var info = mapFromLineView(view, line, pos.line) 8485 8486 var order = getOrder(line), side = "left" 8487 if (order) { 8488 var partPos = getBidiPartAt(order, pos.ch) 8489 side = partPos % 2 ? "right" : "left" 8490 } 8491 var result = nodeAndOffsetInLineMap(info.map, pos.ch, side) 8492 result.offset = result.collapse == "right" ? result.end : result.start 8493 return result 8494 } 8495 8496 function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } 8497 8498 function domTextBetween(cm, from, to, fromLine, toLine) { 8499 var text = "", closing = false, lineSep = cm.doc.lineSeparator() 8500 function recognizeMarker(id) { return function (marker) { return marker.id == id; } } 8501 function walk(node) { 8502 if (node.nodeType == 1) { 8503 var cmText = node.getAttribute("cm-text") 8504 if (cmText != null) { 8505 if (cmText == "") { text += node.textContent.replace(/\u200b/g, "") } 8506 else { text += cmText } 8507 return 8508 } 8509 var markerID = node.getAttribute("cm-marker"), range 8510 if (markerID) { 8511 var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)) 8512 if (found.length && (range = found[0].find())) 8513 { text += getBetween(cm.doc, range.from, range.to).join(lineSep) } 8514 return 8515 } 8516 if (node.getAttribute("contenteditable") == "false") { return } 8517 for (var i = 0; i < node.childNodes.length; i++) 8518 { walk(node.childNodes[i]) } 8519 if (/^(pre|div|p)$/i.test(node.nodeName)) 8520 { closing = true } 8521 } else if (node.nodeType == 3) { 8522 var val = node.nodeValue 8523 if (!val) { return } 8524 if (closing) { 8525 text += lineSep 8526 closing = false 8527 } 8528 text += val 8529 } 8530 } 8531 for (;;) { 8532 walk(from) 8533 if (from == to) { break } 8534 from = from.nextSibling 8535 } 8536 return text 8537 } 8538 8539 function domToPos(cm, node, offset) { 8540 var lineNode 8541 if (node == cm.display.lineDiv) { 8542 lineNode = cm.display.lineDiv.childNodes[offset] 8543 if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } 8544 node = null; offset = 0 8545 } else { 8546 for (lineNode = node;; lineNode = lineNode.parentNode) { 8547 if (!lineNode || lineNode == cm.display.lineDiv) { return null } 8548 if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } 8549 } 8550 } 8551 for (var i = 0; i < cm.display.view.length; i++) { 8552 var lineView = cm.display.view[i] 8553 if (lineView.node == lineNode) 8554 { return locateNodeInLineView(lineView, node, offset) } 8555 } 8556 } 8557 8558 function locateNodeInLineView(lineView, node, offset) { 8559 var wrapper = lineView.text.firstChild, bad = false 8560 if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } 8561 if (node == wrapper) { 8562 bad = true 8563 node = wrapper.childNodes[offset] 8564 offset = 0 8565 if (!node) { 8566 var line = lineView.rest ? lst(lineView.rest) : lineView.line 8567 return badPos(Pos(lineNo(line), line.text.length), bad) 8568 } 8569 } 8570 8571 var textNode = node.nodeType == 3 ? node : null, topNode = node 8572 if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { 8573 textNode = node.firstChild 8574 if (offset) { offset = textNode.nodeValue.length } 8575 } 8576 while (topNode.parentNode != wrapper) { topNode = topNode.parentNode } 8577 var measure = lineView.measure, maps = measure.maps 8578 8579 function find(textNode, topNode, offset) { 8580 for (var i = -1; i < (maps ? maps.length : 0); i++) { 8581 var map = i < 0 ? measure.map : maps[i] 8582 for (var j = 0; j < map.length; j += 3) { 8583 var curNode = map[j + 2] 8584 if (curNode == textNode || curNode == topNode) { 8585 var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]) 8586 var ch = map[j] + offset 8587 if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)] } 8588 return Pos(line, ch) 8589 } 8590 } 8591 } 8592 } 8593 var found = find(textNode, topNode, offset) 8594 if (found) { return badPos(found, bad) } 8595 8596 // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems 8597 for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { 8598 found = find(after, after.firstChild, 0) 8599 if (found) 8600 { return badPos(Pos(found.line, found.ch - dist), bad) } 8601 else 8602 { dist += after.textContent.length } 8603 } 8604 for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { 8605 found = find(before, before.firstChild, -1) 8606 if (found) 8607 { return badPos(Pos(found.line, found.ch + dist$1), bad) } 8608 else 8609 { dist$1 += before.textContent.length } 8610 } 8611 } 8612 8613 // TEXTAREA INPUT STYLE 8614 8615 var TextareaInput = function(cm) { 8616 this.cm = cm 8617 // See input.poll and input.reset 8618 this.prevInput = "" 8619 8620 // Flag that indicates whether we expect input to appear real soon 8621 // now (after some event like 'keypress' or 'input') and are 8622 // polling intensively. 8623 this.pollingFast = false 8624 // Self-resetting timeout for the poller 8625 this.polling = new Delayed() 8626 // Tracks when input.reset has punted to just putting a short 8627 // string into the textarea instead of the full selection. 8628 this.inaccurateSelection = false 8629 // Used to work around IE issue with selection being forgotten when focus moves away from textarea 8630 this.hasSelection = false 8631 this.composing = null 8632 }; 8633 8634 TextareaInput.prototype.init = function (display) { 8635 var this$1 = this; 8636 8637 var input = this, cm = this.cm 8638 8639 // Wraps and hides input textarea 8640 var div = this.wrapper = hiddenTextarea() 8641 // The semihidden textarea that is focused when the editor is 8642 // focused, and receives input. 8643 var te = this.textarea = div.firstChild 8644 display.wrapper.insertBefore(div, display.wrapper.firstChild) 8645 8646 // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) 8647 if (ios) { te.style.width = "0px" } 8648 8649 on(te, "input", function () { 8650 if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null } 8651 input.poll() 8652 }) 8653 8654 on(te, "paste", function (e) { 8655 if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } 8656 8657 cm.state.pasteIncoming = true 8658 input.fastPoll() 8659 }) 8660 8661 function prepareCopyCut(e) { 8662 if (signalDOMEvent(cm, e)) { return } 8663 if (cm.somethingSelected()) { 8664 setLastCopied({lineWise: false, text: cm.getSelections()}) 8665 if (input.inaccurateSelection) { 8666 input.prevInput = "" 8667 input.inaccurateSelection = false 8668 te.value = lastCopied.text.join("\n") 8669 selectInput(te) 8670 } 8671 } else if (!cm.options.lineWiseCopyCut) { 8672 return 8673 } else { 8674 var ranges = copyableRanges(cm) 8675 setLastCopied({lineWise: true, text: ranges.text}) 8676 if (e.type == "cut") { 8677 cm.setSelections(ranges.ranges, null, sel_dontScroll) 8678 } else { 8679 input.prevInput = "" 8680 te.value = ranges.text.join("\n") 8681 selectInput(te) 8682 } 8683 } 8684 if (e.type == "cut") { cm.state.cutIncoming = true } 8685 } 8686 on(te, "cut", prepareCopyCut) 8687 on(te, "copy", prepareCopyCut) 8688 8689 on(display.scroller, "paste", function (e) { 8690 if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } 8691 cm.state.pasteIncoming = true 8692 input.focus() 8693 }) 8694 8695 // Prevent normal selection in the editor (we handle our own) 8696 on(display.lineSpace, "selectstart", function (e) { 8697 if (!eventInWidget(display, e)) { e_preventDefault(e) } 8698 }) 8699 8700 on(te, "compositionstart", function () { 8701 var start = cm.getCursor("from") 8702 if (input.composing) { input.composing.range.clear() } 8703 input.composing = { 8704 start: start, 8705 range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) 8706 } 8707 }) 8708 on(te, "compositionend", function () { 8709 if (input.composing) { 8710 input.poll() 8711 input.composing.range.clear() 8712 input.composing = null 8713 } 8714 }) 8715 }; 8716 8717 TextareaInput.prototype.prepareSelection = function () { 8718 // Redraw the selection and/or cursor 8719 var cm = this.cm, display = cm.display, doc = cm.doc 8720 var result = prepareSelection(cm) 8721 8722 // Move the hidden textarea near the cursor to prevent scrolling artifacts 8723 if (cm.options.moveInputWithCursor) { 8724 var headPos = cursorCoords(cm, doc.sel.primary().head, "div") 8725 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect() 8726 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, 8727 headPos.top + lineOff.top - wrapOff.top)) 8728 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, 8729 headPos.left + lineOff.left - wrapOff.left)) 8730 } 8731 8732 return result 8733 }; 8734 8735 TextareaInput.prototype.showSelection = function (drawn) { 8736 var cm = this.cm, display = cm.display 8737 removeChildrenAndAdd(display.cursorDiv, drawn.cursors) 8738 removeChildrenAndAdd(display.selectionDiv, drawn.selection) 8739 if (drawn.teTop != null) { 8740 this.wrapper.style.top = drawn.teTop + "px" 8741 this.wrapper.style.left = drawn.teLeft + "px" 8742 } 8743 }; 8744 8745 // Reset the input to correspond to the selection (or to be empty, 8746 // when not typing and nothing is selected) 8747 TextareaInput.prototype.reset = function (typing) { 8748 if (this.contextMenuPending) { return } 8749 var minimal, selected, cm = this.cm, doc = cm.doc 8750 if (cm.somethingSelected()) { 8751 this.prevInput = "" 8752 var range = doc.sel.primary() 8753 minimal = hasCopyEvent && 8754 (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000) 8755 var content = minimal ? "-" : selected || cm.getSelection() 8756 this.textarea.value = content 8757 if (cm.state.focused) { selectInput(this.textarea) } 8758 if (ie && ie_version >= 9) { this.hasSelection = content } 8759 } else if (!typing) { 8760 this.prevInput = this.textarea.value = "" 8761 if (ie && ie_version >= 9) { this.hasSelection = null } 8762 } 8763 this.inaccurateSelection = minimal 8764 }; 8765 8766 TextareaInput.prototype.getField = function () { return this.textarea }; 8767 8768 TextareaInput.prototype.supportsTouch = function () { return false }; 8769 8770 TextareaInput.prototype.focus = function () { 8771 if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { 8772 try { this.textarea.focus() } 8773 catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM 8774 } 8775 }; 8776 8777 TextareaInput.prototype.blur = function () { this.textarea.blur() }; 8778 8779 TextareaInput.prototype.resetPosition = function () { 8780 this.wrapper.style.top = this.wrapper.style.left = 0 8781 }; 8782 8783 TextareaInput.prototype.receivedFocus = function () { this.slowPoll() }; 8784 8785 // Poll for input changes, using the normal rate of polling. This 8786 // runs as long as the editor is focused. 8787 TextareaInput.prototype.slowPoll = function () { 8788 var this$1 = this; 8789 8790 if (this.pollingFast) { return } 8791 this.polling.set(this.cm.options.pollInterval, function () { 8792 this$1.poll() 8793 if (this$1.cm.state.focused) { this$1.slowPoll() } 8794 }) 8795 }; 8796 8797 // When an event has just come in that is likely to add or change 8798 // something in the input textarea, we poll faster, to ensure that 8799 // the change appears on the screen quickly. 8800 TextareaInput.prototype.fastPoll = function () { 8801 var missed = false, input = this 8802 input.pollingFast = true 8803 function p() { 8804 var changed = input.poll() 8805 if (!changed && !missed) {missed = true; input.polling.set(60, p)} 8806 else {input.pollingFast = false; input.slowPoll()} 8807 } 8808 input.polling.set(20, p) 8809 }; 8810 8811 // Read input from the textarea, and update the document to match. 8812 // When something is selected, it is present in the textarea, and 8813 // selected (unless it is huge, in which case a placeholder is 8814 // used). When nothing is selected, the cursor sits after previously 8815 // seen text (can be empty), which is stored in prevInput (we must 8816 // not reset the textarea when typing, because that breaks IME). 8817 TextareaInput.prototype.poll = function () { 8818 var this$1 = this; 8819 8820 var cm = this.cm, input = this.textarea, prevInput = this.prevInput 8821 // Since this is called a *lot*, try to bail out as cheaply as 8822 // possible when it is clear that nothing happened. hasSelection 8823 // will be the case when there is a lot of text in the textarea, 8824 // in which case reading its value would be expensive. 8825 if (this.contextMenuPending || !cm.state.focused || 8826 (hasSelection(input) && !prevInput && !this.composing) || 8827 cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) 8828 { return false } 8829 8830 var text = input.value 8831 // If nothing changed, bail. 8832 if (text == prevInput && !cm.somethingSelected()) { return false } 8833 // Work around nonsensical selection resetting in IE9/10, and 8834 // inexplicable appearance of private area unicode characters on 8835 // some key combos in Mac (#2689). 8836 if (ie && ie_version >= 9 && this.hasSelection === text || 8837 mac && /[\uf700-\uf7ff]/.test(text)) { 8838 cm.display.input.reset() 8839 return false 8840 } 8841 8842 if (cm.doc.sel == cm.display.selForContextMenu) { 8843 var first = text.charCodeAt(0) 8844 if (first == 0x200b && !prevInput) { prevInput = "\u200b" } 8845 if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } 8846 } 8847 // Find the part of the input that is actually new 8848 var same = 0, l = Math.min(prevInput.length, text.length) 8849 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same } 8850 8851 runInOp(cm, function () { 8852 applyTextInput(cm, text.slice(same), prevInput.length - same, 8853 null, this$1.composing ? "*compose" : null) 8854 8855 // Don't leave long text in the textarea, since it makes further polling slow 8856 if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" } 8857 else { this$1.prevInput = text } 8858 8859 if (this$1.composing) { 8860 this$1.composing.range.clear() 8861 this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), 8862 {className: "CodeMirror-composing"}) 8863 } 8864 }) 8865 return true 8866 }; 8867 8868 TextareaInput.prototype.ensurePolled = function () { 8869 if (this.pollingFast && this.poll()) { this.pollingFast = false } 8870 }; 8871 8872 TextareaInput.prototype.onKeyPress = function () { 8873 if (ie && ie_version >= 9) { this.hasSelection = null } 8874 this.fastPoll() 8875 }; 8876 8877 TextareaInput.prototype.onContextMenu = function (e) { 8878 var input = this, cm = input.cm, display = cm.display, te = input.textarea 8879 var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop 8880 if (!pos || presto) { return } // Opera is difficult. 8881 8882 // Reset the current text selection only if the click is done outside of the selection 8883 // and 'resetSelectionOnContextMenu' option is true. 8884 var reset = cm.options.resetSelectionOnContextMenu 8885 if (reset && cm.doc.sel.contains(pos) == -1) 8886 { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) } 8887 8888 var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText 8889 input.wrapper.style.cssText = "position: absolute" 8890 var wrapperBox = input.wrapper.getBoundingClientRect() 8891 te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);" 8892 var oldScrollY 8893 if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712) 8894 display.input.focus() 8895 if (webkit) { window.scrollTo(null, oldScrollY) } 8896 display.input.reset() 8897 // Adds "Select all" to context menu in FF 8898 if (!cm.somethingSelected()) { te.value = input.prevInput = " " } 8899 input.contextMenuPending = true 8900 display.selForContextMenu = cm.doc.sel 8901 clearTimeout(display.detectingSelectAll) 8902 8903 // Select-all will be greyed out if there's nothing to select, so 8904 // this adds a zero-width space so that we can later check whether 8905 // it got selected. 8906 function prepareSelectAllHack() { 8907 if (te.selectionStart != null) { 8908 var selected = cm.somethingSelected() 8909 var extval = "\u200b" + (selected ? te.value : "") 8910 te.value = "\u21da" // Used to catch context-menu undo 8911 te.value = extval 8912 input.prevInput = selected ? "" : "\u200b" 8913 te.selectionStart = 1; te.selectionEnd = extval.length 8914 // Re-set this, in case some other handler touched the 8915 // selection in the meantime. 8916 display.selForContextMenu = cm.doc.sel 8917 } 8918 } 8919 function rehide() { 8920 input.contextMenuPending = false 8921 input.wrapper.style.cssText = oldWrapperCSS 8922 te.style.cssText = oldCSS 8923 if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) } 8924 8925 // Try to detect the user choosing select-all 8926 if (te.selectionStart != null) { 8927 if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() } 8928 var i = 0, poll = function () { 8929 if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && 8930 te.selectionEnd > 0 && input.prevInput == "\u200b") 8931 { operation(cm, selectAll)(cm) } 8932 else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500) } 8933 else { display.input.reset() } 8934 } 8935 display.detectingSelectAll = setTimeout(poll, 200) 8936 } 8937 } 8938 8939 if (ie && ie_version >= 9) { prepareSelectAllHack() } 8940 if (captureRightClick) { 8941 e_stop(e) 8942 var mouseup = function () { 8943 off(window, "mouseup", mouseup) 8944 setTimeout(rehide, 20) 8945 } 8946 on(window, "mouseup", mouseup) 8947 } else { 8948 setTimeout(rehide, 50) 8949 } 8950 }; 8951 8952 TextareaInput.prototype.readOnlyChanged = function (val) { 8953 if (!val) { this.reset() } 8954 }; 8955 8956 TextareaInput.prototype.setUneditable = function () {}; 8957 8958 TextareaInput.prototype.needsContentAttribute = false 8959 8960 function fromTextArea(textarea, options) { 8961 options = options ? copyObj(options) : {} 8962 options.value = textarea.value 8963 if (!options.tabindex && textarea.tabIndex) 8964 { options.tabindex = textarea.tabIndex } 8965 if (!options.placeholder && textarea.placeholder) 8966 { options.placeholder = textarea.placeholder } 8967 // Set autofocus to true if this textarea is focused, or if it has 8968 // autofocus and no other element is focused. 8969 if (options.autofocus == null) { 8970 var hasFocus = activeElt() 8971 options.autofocus = hasFocus == textarea || 8972 textarea.getAttribute("autofocus") != null && hasFocus == document.body 8973 } 8974 8975 function save() {textarea.value = cm.getValue()} 8976 8977 var realSubmit 8978 if (textarea.form) { 8979 on(textarea.form, "submit", save) 8980 // Deplorable hack to make the submit method do the right thing. 8981 if (!options.leaveSubmitMethodAlone) { 8982 var form = textarea.form 8983 realSubmit = form.submit 8984 try { 8985 var wrappedSubmit = form.submit = function () { 8986 save() 8987 form.submit = realSubmit 8988 form.submit() 8989 form.submit = wrappedSubmit 8990 } 8991 } catch(e) {} 8992 } 8993 } 8994 8995 options.finishInit = function (cm) { 8996 cm.save = save 8997 cm.getTextArea = function () { return textarea; } 8998 cm.toTextArea = function () { 8999 cm.toTextArea = isNaN // Prevent this from being ran twice 9000 save() 9001 textarea.parentNode.removeChild(cm.getWrapperElement()) 9002 textarea.style.display = "" 9003 if (textarea.form) { 9004 off(textarea.form, "submit", save) 9005 if (typeof textarea.form.submit == "function") 9006 { textarea.form.submit = realSubmit } 9007 } 9008 } 9009 } 9010 9011 textarea.style.display = "none" 9012 var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, 9013 options) 9014 return cm 9015 } 9016 9017 function addLegacyProps(CodeMirror) { 9018 CodeMirror.off = off 9019 CodeMirror.on = on 9020 CodeMirror.wheelEventPixels = wheelEventPixels 9021 CodeMirror.Doc = Doc 9022 CodeMirror.splitLines = splitLinesAuto 9023 CodeMirror.countColumn = countColumn 9024 CodeMirror.findColumn = findColumn 9025 CodeMirror.isWordChar = isWordCharBasic 9026 CodeMirror.Pass = Pass 9027 CodeMirror.signal = signal 9028 CodeMirror.Line = Line 9029 CodeMirror.changeEnd = changeEnd 9030 CodeMirror.scrollbarModel = scrollbarModel 9031 CodeMirror.Pos = Pos 9032 CodeMirror.cmpPos = cmp 9033 CodeMirror.modes = modes 9034 CodeMirror.mimeModes = mimeModes 9035 CodeMirror.resolveMode = resolveMode 9036 CodeMirror.getMode = getMode 9037 CodeMirror.modeExtensions = modeExtensions 9038 CodeMirror.extendMode = extendMode 9039 CodeMirror.copyState = copyState 9040 CodeMirror.startState = startState 9041 CodeMirror.innerMode = innerMode 9042 CodeMirror.commands = commands 9043 CodeMirror.keyMap = keyMap 9044 CodeMirror.keyName = keyName 9045 CodeMirror.isModifierKey = isModifierKey 9046 CodeMirror.lookupKey = lookupKey 9047 CodeMirror.normalizeKeyMap = normalizeKeyMap 9048 CodeMirror.StringStream = StringStream 9049 CodeMirror.SharedTextMarker = SharedTextMarker 9050 CodeMirror.TextMarker = TextMarker 9051 CodeMirror.LineWidget = LineWidget 9052 CodeMirror.e_preventDefault = e_preventDefault 9053 CodeMirror.e_stopPropagation = e_stopPropagation 9054 CodeMirror.e_stop = e_stop 9055 CodeMirror.addClass = addClass 9056 CodeMirror.contains = contains 9057 CodeMirror.rmClass = rmClass 9058 CodeMirror.keyNames = keyNames 9059 } 9060 9061 // EDITOR CONSTRUCTOR 9062 9063 defineOptions(CodeMirror) 9064 9065 addEditorMethods(CodeMirror) 9066 9067 // Set up methods on CodeMirror's prototype to redirect to the editor's document. 9068 var dontDelegate = "iter insert remove copy getEditor constructor".split(" ") 9069 for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) 9070 { CodeMirror.prototype[prop] = (function(method) { 9071 return function() {return method.apply(this.doc, arguments)} 9072 })(Doc.prototype[prop]) } } 9073 9074 eventMixin(Doc) 9075 9076 // INPUT HANDLING 9077 9078 CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput} 9079 9080 // MODE DEFINITION AND QUERYING 9081 9082 // Extra arguments are stored as the mode's dependencies, which is 9083 // used by (legacy) mechanisms like loadmode.js to automatically 9084 // load a mode. (Preferred mechanism is the require/define calls.) 9085 CodeMirror.defineMode = function(name/*, mode, …*/) { 9086 if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name } 9087 defineMode.apply(this, arguments) 9088 } 9089 9090 CodeMirror.defineMIME = defineMIME 9091 9092 // Minimal default mode. 9093 CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }) 9094 CodeMirror.defineMIME("text/plain", "null") 9095 9096 // EXTENSIONS 9097 9098 CodeMirror.defineExtension = function (name, func) { 9099 CodeMirror.prototype[name] = func 9100 } 9101 CodeMirror.defineDocExtension = function (name, func) { 9102 Doc.prototype[name] = func 9103 } 9104 9105 CodeMirror.fromTextArea = fromTextArea 9106 9107 addLegacyProps(CodeMirror) 9108 9109 CodeMirror.version = "5.23.0" 9110 9111 return CodeMirror; 9112 9113 }))); 9114 No newline at end of file -
src/wp-includes/js/codemirror/mode/clike/clike.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 function Context(indented, column, type, info, align, prev) { 15 this.indented = indented; 16 this.column = column; 17 this.type = type; 18 this.info = info; 19 this.align = align; 20 this.prev = prev; 21 } 22 function pushContext(state, col, type, info) { 23 var indent = state.indented; 24 if (state.context && state.context.type == "statement" && type != "statement") 25 indent = state.context.indented; 26 return state.context = new Context(indent, col, type, info, null, state.context); 27 } 28 function popContext(state) { 29 var t = state.context.type; 30 if (t == ")" || t == "]" || t == "}") 31 state.indented = state.context.indented; 32 return state.context = state.context.prev; 33 } 34 35 function typeBefore(stream, state, pos) { 36 if (state.prevToken == "variable" || state.prevToken == "variable-3") return true; 37 if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true; 38 if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true; 39 } 40 41 function isTopScope(context) { 42 for (;;) { 43 if (!context || context.type == "top") return true; 44 if (context.type == "}" && context.prev.info != "namespace") return false; 45 context = context.prev; 46 } 47 } 48 49 CodeMirror.defineMode("clike", function(config, parserConfig) { 50 var indentUnit = config.indentUnit, 51 statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, 52 dontAlignCalls = parserConfig.dontAlignCalls, 53 keywords = parserConfig.keywords || {}, 54 types = parserConfig.types || {}, 55 builtin = parserConfig.builtin || {}, 56 blockKeywords = parserConfig.blockKeywords || {}, 57 defKeywords = parserConfig.defKeywords || {}, 58 atoms = parserConfig.atoms || {}, 59 hooks = parserConfig.hooks || {}, 60 multiLineStrings = parserConfig.multiLineStrings, 61 indentStatements = parserConfig.indentStatements !== false, 62 indentSwitch = parserConfig.indentSwitch !== false, 63 namespaceSeparator = parserConfig.namespaceSeparator, 64 isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/, 65 numberStart = parserConfig.numberStart || /[\d\.]/, 66 number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i, 67 isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/; 68 69 var curPunc, isDefKeyword; 70 71 function tokenBase(stream, state) { 72 var ch = stream.next(); 73 if (hooks[ch]) { 74 var result = hooks[ch](stream, state); 75 if (result !== false) return result; 76 } 77 if (ch == '"' || ch == "'") { 78 state.tokenize = tokenString(ch); 79 return state.tokenize(stream, state); 80 } 81 if (isPunctuationChar.test(ch)) { 82 curPunc = ch; 83 return null; 84 } 85 if (numberStart.test(ch)) { 86 stream.backUp(1) 87 if (stream.match(number)) return "number" 88 stream.next() 89 } 90 if (ch == "/") { 91 if (stream.eat("*")) { 92 state.tokenize = tokenComment; 93 return tokenComment(stream, state); 94 } 95 if (stream.eat("/")) { 96 stream.skipToEnd(); 97 return "comment"; 98 } 99 } 100 if (isOperatorChar.test(ch)) { 101 while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {} 102 return "operator"; 103 } 104 stream.eatWhile(/[\w\$_\xa1-\uffff]/); 105 if (namespaceSeparator) while (stream.match(namespaceSeparator)) 106 stream.eatWhile(/[\w\$_\xa1-\uffff]/); 107 108 var cur = stream.current(); 109 if (contains(keywords, cur)) { 110 if (contains(blockKeywords, cur)) curPunc = "newstatement"; 111 if (contains(defKeywords, cur)) isDefKeyword = true; 112 return "keyword"; 113 } 114 if (contains(types, cur)) return "variable-3"; 115 if (contains(builtin, cur)) { 116 if (contains(blockKeywords, cur)) curPunc = "newstatement"; 117 return "builtin"; 118 } 119 if (contains(atoms, cur)) return "atom"; 120 return "variable"; 121 } 122 123 function tokenString(quote) { 124 return function(stream, state) { 125 var escaped = false, next, end = false; 126 while ((next = stream.next()) != null) { 127 if (next == quote && !escaped) {end = true; break;} 128 escaped = !escaped && next == "\\"; 129 } 130 if (end || !(escaped || multiLineStrings)) 131 state.tokenize = null; 132 return "string"; 133 }; 134 } 135 136 function tokenComment(stream, state) { 137 var maybeEnd = false, ch; 138 while (ch = stream.next()) { 139 if (ch == "/" && maybeEnd) { 140 state.tokenize = null; 141 break; 142 } 143 maybeEnd = (ch == "*"); 144 } 145 return "comment"; 146 } 147 148 function maybeEOL(stream, state) { 149 if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context)) 150 state.typeAtEndOfLine = typeBefore(stream, state, stream.pos) 151 } 152 153 // Interface 154 155 return { 156 startState: function(basecolumn) { 157 return { 158 tokenize: null, 159 context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false), 160 indented: 0, 161 startOfLine: true, 162 prevToken: null 163 }; 164 }, 165 166 token: function(stream, state) { 167 var ctx = state.context; 168 if (stream.sol()) { 169 if (ctx.align == null) ctx.align = false; 170 state.indented = stream.indentation(); 171 state.startOfLine = true; 172 } 173 if (stream.eatSpace()) { maybeEOL(stream, state); return null; } 174 curPunc = isDefKeyword = null; 175 var style = (state.tokenize || tokenBase)(stream, state); 176 if (style == "comment" || style == "meta") return style; 177 if (ctx.align == null) ctx.align = true; 178 179 if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false))) 180 while (state.context.type == "statement") popContext(state); 181 else if (curPunc == "{") pushContext(state, stream.column(), "}"); 182 else if (curPunc == "[") pushContext(state, stream.column(), "]"); 183 else if (curPunc == "(") pushContext(state, stream.column(), ")"); 184 else if (curPunc == "}") { 185 while (ctx.type == "statement") ctx = popContext(state); 186 if (ctx.type == "}") ctx = popContext(state); 187 while (ctx.type == "statement") ctx = popContext(state); 188 } 189 else if (curPunc == ctx.type) popContext(state); 190 else if (indentStatements && 191 (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") || 192 (ctx.type == "statement" && curPunc == "newstatement"))) { 193 pushContext(state, stream.column(), "statement", stream.current()); 194 } 195 196 if (style == "variable" && 197 ((state.prevToken == "def" || 198 (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) && 199 isTopScope(state.context) && stream.match(/^\s*\(/, false))))) 200 style = "def"; 201 202 if (hooks.token) { 203 var result = hooks.token(stream, state, style); 204 if (result !== undefined) style = result; 205 } 206 207 if (style == "def" && parserConfig.styleDefs === false) style = "variable"; 208 209 state.startOfLine = false; 210 state.prevToken = isDefKeyword ? "def" : style || curPunc; 211 maybeEOL(stream, state); 212 return style; 213 }, 214 215 indent: function(state, textAfter) { 216 if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass; 217 var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); 218 if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; 219 if (parserConfig.dontIndentStatements) 220 while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info)) 221 ctx = ctx.prev 222 if (hooks.indent) { 223 var hook = hooks.indent(state, ctx, textAfter); 224 if (typeof hook == "number") return hook 225 } 226 var closing = firstChar == ctx.type; 227 var switchBlock = ctx.prev && ctx.prev.info == "switch"; 228 if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) { 229 while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev 230 return ctx.indented 231 } 232 if (ctx.type == "statement") 233 return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); 234 if (ctx.align && (!dontAlignCalls || ctx.type != ")")) 235 return ctx.column + (closing ? 0 : 1); 236 if (ctx.type == ")" && !closing) 237 return ctx.indented + statementIndentUnit; 238 239 return ctx.indented + (closing ? 0 : indentUnit) + 240 (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0); 241 }, 242 243 electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/, 244 blockCommentStart: "/*", 245 blockCommentEnd: "*/", 246 lineComment: "//", 247 fold: "brace" 248 }; 249 }); 250 251 function words(str) { 252 var obj = {}, words = str.split(" "); 253 for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 254 return obj; 255 } 256 function contains(words, word) { 257 if (typeof words === "function") { 258 return words(word); 259 } else { 260 return words.propertyIsEnumerable(word); 261 } 262 } 263 var cKeywords = "auto if break case register continue return default do sizeof " + 264 "static else struct switch extern typedef union for goto while enum const volatile"; 265 var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t"; 266 267 function cppHook(stream, state) { 268 if (!state.startOfLine) return false 269 for (var ch, next = null; ch = stream.peek();) { 270 if (ch == "\\" && stream.match(/^.$/)) { 271 next = cppHook 272 break 273 } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) { 274 break 275 } 276 stream.next() 277 } 278 state.tokenize = next 279 return "meta" 280 } 281 282 function pointerHook(_stream, state) { 283 if (state.prevToken == "variable-3") return "variable-3"; 284 return false; 285 } 286 287 function cpp14Literal(stream) { 288 stream.eatWhile(/[\w\.']/); 289 return "number"; 290 } 291 292 function cpp11StringHook(stream, state) { 293 stream.backUp(1); 294 // Raw strings. 295 if (stream.match(/(R|u8R|uR|UR|LR)/)) { 296 var match = stream.match(/"([^\s\\()]{0,16})\(/); 297 if (!match) { 298 return false; 299 } 300 state.cpp11RawStringDelim = match[1]; 301 state.tokenize = tokenRawString; 302 return tokenRawString(stream, state); 303 } 304 // Unicode strings/chars. 305 if (stream.match(/(u8|u|U|L)/)) { 306 if (stream.match(/["']/, /* eat */ false)) { 307 return "string"; 308 } 309 return false; 310 } 311 // Ignore this hook. 312 stream.next(); 313 return false; 314 } 315 316 function cppLooksLikeConstructor(word) { 317 var lastTwo = /(\w+)::(\w+)$/.exec(word); 318 return lastTwo && lastTwo[1] == lastTwo[2]; 319 } 320 321 // C#-style strings where "" escapes a quote. 322 function tokenAtString(stream, state) { 323 var next; 324 while ((next = stream.next()) != null) { 325 if (next == '"' && !stream.eat('"')) { 326 state.tokenize = null; 327 break; 328 } 329 } 330 return "string"; 331 } 332 333 // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where 334 // <delim> can be a string up to 16 characters long. 335 function tokenRawString(stream, state) { 336 // Escape characters that have special regex meanings. 337 var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&'); 338 var match = stream.match(new RegExp(".*?\\)" + delim + '"')); 339 if (match) 340 state.tokenize = null; 341 else 342 stream.skipToEnd(); 343 return "string"; 344 } 345 346 function def(mimes, mode) { 347 if (typeof mimes == "string") mimes = [mimes]; 348 var words = []; 349 function add(obj) { 350 if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop)) 351 words.push(prop); 352 } 353 add(mode.keywords); 354 add(mode.types); 355 add(mode.builtin); 356 add(mode.atoms); 357 if (words.length) { 358 mode.helperType = mimes[0]; 359 CodeMirror.registerHelper("hintWords", mimes[0], words); 360 } 361 362 for (var i = 0; i < mimes.length; ++i) 363 CodeMirror.defineMIME(mimes[i], mode); 364 } 365 366 def(["text/x-csrc", "text/x-c", "text/x-chdr"], { 367 name: "clike", 368 keywords: words(cKeywords), 369 types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " + 370 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " + 371 "uint32_t uint64_t"), 372 blockKeywords: words("case do else for if switch while struct"), 373 defKeywords: words("struct"), 374 typeFirstDefinitions: true, 375 atoms: words("null true false"), 376 hooks: {"#": cppHook, "*": pointerHook}, 377 modeProps: {fold: ["brace", "include"]} 378 }); 379 380 def(["text/x-c++src", "text/x-c++hdr"], { 381 name: "clike", 382 keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " + 383 "static_cast typeid catch operator template typename class friend private " + 384 "this using const_cast inline public throw virtual delete mutable protected " + 385 "alignas alignof constexpr decltype nullptr noexcept thread_local final " + 386 "static_assert override"), 387 types: words(cTypes + " bool wchar_t"), 388 blockKeywords: words("catch class do else finally for if struct switch try while"), 389 defKeywords: words("class namespace struct enum union"), 390 typeFirstDefinitions: true, 391 atoms: words("true false null"), 392 dontIndentStatements: /^template$/, 393 hooks: { 394 "#": cppHook, 395 "*": pointerHook, 396 "u": cpp11StringHook, 397 "U": cpp11StringHook, 398 "L": cpp11StringHook, 399 "R": cpp11StringHook, 400 "0": cpp14Literal, 401 "1": cpp14Literal, 402 "2": cpp14Literal, 403 "3": cpp14Literal, 404 "4": cpp14Literal, 405 "5": cpp14Literal, 406 "6": cpp14Literal, 407 "7": cpp14Literal, 408 "8": cpp14Literal, 409 "9": cpp14Literal, 410 token: function(stream, state, style) { 411 if (style == "variable" && stream.peek() == "(" && 412 (state.prevToken == ";" || state.prevToken == null || 413 state.prevToken == "}") && 414 cppLooksLikeConstructor(stream.current())) 415 return "def"; 416 } 417 }, 418 namespaceSeparator: "::", 419 modeProps: {fold: ["brace", "include"]} 420 }); 421 422 def("text/x-java", { 423 name: "clike", 424 keywords: words("abstract assert break case catch class const continue default " + 425 "do else enum extends final finally float for goto if implements import " + 426 "instanceof interface native new package private protected public " + 427 "return static strictfp super switch synchronized this throw throws transient " + 428 "try volatile while"), 429 types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " + 430 "Integer Long Number Object Short String StringBuffer StringBuilder Void"), 431 blockKeywords: words("catch class do else finally for if switch try while"), 432 defKeywords: words("class interface package enum"), 433 typeFirstDefinitions: true, 434 atoms: words("true false null"), 435 number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, 436 hooks: { 437 "@": function(stream) { 438 stream.eatWhile(/[\w\$_]/); 439 return "meta"; 440 } 441 }, 442 modeProps: {fold: ["brace", "import"]} 443 }); 444 445 def("text/x-csharp", { 446 name: "clike", 447 keywords: words("abstract as async await base break case catch checked class const continue" + 448 " default delegate do else enum event explicit extern finally fixed for" + 449 " foreach goto if implicit in interface internal is lock namespace new" + 450 " operator out override params private protected public readonly ref return sealed" + 451 " sizeof stackalloc static struct switch this throw try typeof unchecked" + 452 " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + 453 " global group into join let orderby partial remove select set value var yield"), 454 types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" + 455 " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" + 456 " UInt64 bool byte char decimal double short int long object" + 457 " sbyte float string ushort uint ulong"), 458 blockKeywords: words("catch class do else finally for foreach if struct switch try while"), 459 defKeywords: words("class interface namespace struct var"), 460 typeFirstDefinitions: true, 461 atoms: words("true false null"), 462 hooks: { 463 "@": function(stream, state) { 464 if (stream.eat('"')) { 465 state.tokenize = tokenAtString; 466 return tokenAtString(stream, state); 467 } 468 stream.eatWhile(/[\w\$_]/); 469 return "meta"; 470 } 471 } 472 }); 473 474 function tokenTripleString(stream, state) { 475 var escaped = false; 476 while (!stream.eol()) { 477 if (!escaped && stream.match('"""')) { 478 state.tokenize = null; 479 break; 480 } 481 escaped = stream.next() == "\\" && !escaped; 482 } 483 return "string"; 484 } 485 486 def("text/x-scala", { 487 name: "clike", 488 keywords: words( 489 490 /* scala */ 491 "abstract case catch class def do else extends final finally for forSome if " + 492 "implicit import lazy match new null object override package private protected return " + 493 "sealed super this throw trait try type val var while with yield _ : = => <- <: " + 494 "<% >: # @ " + 495 496 /* package scala */ 497 "assert assume require print println printf readLine readBoolean readByte readShort " + 498 "readChar readInt readLong readFloat readDouble " + 499 500 ":: #:: " 501 ), 502 types: words( 503 "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " + 504 "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " + 505 "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " + 506 "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " + 507 "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " + 508 509 /* package java.lang */ 510 "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + 511 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + 512 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + 513 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" 514 ), 515 multiLineStrings: true, 516 blockKeywords: words("catch class do else finally for forSome if match switch try while"), 517 defKeywords: words("class def object package trait type val var"), 518 atoms: words("true false null"), 519 indentStatements: false, 520 indentSwitch: false, 521 hooks: { 522 "@": function(stream) { 523 stream.eatWhile(/[\w\$_]/); 524 return "meta"; 525 }, 526 '"': function(stream, state) { 527 if (!stream.match('""')) return false; 528 state.tokenize = tokenTripleString; 529 return state.tokenize(stream, state); 530 }, 531 "'": function(stream) { 532 stream.eatWhile(/[\w\$_\xa1-\uffff]/); 533 return "atom"; 534 }, 535 "=": function(stream, state) { 536 var cx = state.context 537 if (cx.type == "}" && cx.align && stream.eat(">")) { 538 state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev) 539 return "operator" 540 } else { 541 return false 542 } 543 } 544 }, 545 modeProps: {closeBrackets: {triples: '"'}} 546 }); 547 548 function tokenKotlinString(tripleString){ 549 return function (stream, state) { 550 var escaped = false, next, end = false; 551 while (!stream.eol()) { 552 if (!tripleString && !escaped && stream.match('"') ) {end = true; break;} 553 if (tripleString && stream.match('"""')) {end = true; break;} 554 next = stream.next(); 555 if(!escaped && next == "$" && stream.match('{')) 556 stream.skipTo("}"); 557 escaped = !escaped && next == "\\" && !tripleString; 558 } 559 if (end || !tripleString) 560 state.tokenize = null; 561 return "string"; 562 } 563 } 564 565 def("text/x-kotlin", { 566 name: "clike", 567 keywords: words( 568 /*keywords*/ 569 "package as typealias class interface this super val " + 570 "var fun for is in This throw return " + 571 "break continue object if else while do try when !in !is as? " + 572 573 /*soft keywords*/ 574 "file import where by get set abstract enum open inner override private public internal " + 575 "protected catch finally out final vararg reified dynamic companion constructor init " + 576 "sealed field property receiver param sparam lateinit data inline noinline tailrec " + 577 "external annotation crossinline const operator infix" 578 ), 579 types: words( 580 /* package java.lang */ 581 "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + 582 "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + 583 "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + 584 "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" 585 ), 586 intendSwitch: false, 587 indentStatements: false, 588 multiLineStrings: true, 589 blockKeywords: words("catch class do else finally for if where try while enum"), 590 defKeywords: words("class val var object package interface fun"), 591 atoms: words("true false null this"), 592 hooks: { 593 '"': function(stream, state) { 594 state.tokenize = tokenKotlinString(stream.match('""')); 595 return state.tokenize(stream, state); 596 } 597 }, 598 modeProps: {closeBrackets: {triples: '"'}} 599 }); 600 601 def(["x-shader/x-vertex", "x-shader/x-fragment"], { 602 name: "clike", 603 keywords: words("sampler1D sampler2D sampler3D samplerCube " + 604 "sampler1DShadow sampler2DShadow " + 605 "const attribute uniform varying " + 606 "break continue discard return " + 607 "for while do if else struct " + 608 "in out inout"), 609 types: words("float int bool void " + 610 "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " + 611 "mat2 mat3 mat4"), 612 blockKeywords: words("for while do if else struct"), 613 builtin: words("radians degrees sin cos tan asin acos atan " + 614 "pow exp log exp2 sqrt inversesqrt " + 615 "abs sign floor ceil fract mod min max clamp mix step smoothstep " + 616 "length distance dot cross normalize ftransform faceforward " + 617 "reflect refract matrixCompMult " + 618 "lessThan lessThanEqual greaterThan greaterThanEqual " + 619 "equal notEqual any all not " + 620 "texture1D texture1DProj texture1DLod texture1DProjLod " + 621 "texture2D texture2DProj texture2DLod texture2DProjLod " + 622 "texture3D texture3DProj texture3DLod texture3DProjLod " + 623 "textureCube textureCubeLod " + 624 "shadow1D shadow2D shadow1DProj shadow2DProj " + 625 "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " + 626 "dFdx dFdy fwidth " + 627 "noise1 noise2 noise3 noise4"), 628 atoms: words("true false " + 629 "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " + 630 "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " + 631 "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " + 632 "gl_FogCoord gl_PointCoord " + 633 "gl_Position gl_PointSize gl_ClipVertex " + 634 "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " + 635 "gl_TexCoord gl_FogFragCoord " + 636 "gl_FragCoord gl_FrontFacing " + 637 "gl_FragData gl_FragDepth " + 638 "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " + 639 "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " + 640 "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " + 641 "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " + 642 "gl_ProjectionMatrixInverseTranspose " + 643 "gl_ModelViewProjectionMatrixInverseTranspose " + 644 "gl_TextureMatrixInverseTranspose " + 645 "gl_NormalScale gl_DepthRange gl_ClipPlane " + 646 "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " + 647 "gl_FrontLightModelProduct gl_BackLightModelProduct " + 648 "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " + 649 "gl_FogParameters " + 650 "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " + 651 "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " + 652 "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " + 653 "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " + 654 "gl_MaxDrawBuffers"), 655 indentSwitch: false, 656 hooks: {"#": cppHook}, 657 modeProps: {fold: ["brace", "include"]} 658 }); 659 660 def("text/x-nesc", { 661 name: "clike", 662 keywords: words(cKeywords + "as atomic async call command component components configuration event generic " + 663 "implementation includes interface module new norace nx_struct nx_union post provides " + 664 "signal task uses abstract extends"), 665 types: words(cTypes), 666 blockKeywords: words("case do else for if switch while struct"), 667 atoms: words("null true false"), 668 hooks: {"#": cppHook}, 669 modeProps: {fold: ["brace", "include"]} 670 }); 671 672 def("text/x-objectivec", { 673 name: "clike", 674 keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in " + 675 "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"), 676 types: words(cTypes), 677 atoms: words("YES NO NULL NILL ON OFF true false"), 678 hooks: { 679 "@": function(stream) { 680 stream.eatWhile(/[\w\$]/); 681 return "keyword"; 682 }, 683 "#": cppHook, 684 indent: function(_state, ctx, textAfter) { 685 if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented 686 } 687 }, 688 modeProps: {fold: "brace"} 689 }); 690 691 def("text/x-squirrel", { 692 name: "clike", 693 keywords: words("base break clone continue const default delete enum extends function in class" + 694 " foreach local resume return this throw typeof yield constructor instanceof static"), 695 types: words(cTypes), 696 blockKeywords: words("case catch class else for foreach if switch try while"), 697 defKeywords: words("function local class"), 698 typeFirstDefinitions: true, 699 atoms: words("true false null"), 700 hooks: {"#": cppHook}, 701 modeProps: {fold: ["brace", "include"]} 702 }); 703 704 // Ceylon Strings need to deal with interpolation 705 var stringTokenizer = null; 706 function tokenCeylonString(type) { 707 return function(stream, state) { 708 var escaped = false, next, end = false; 709 while (!stream.eol()) { 710 if (!escaped && stream.match('"') && 711 (type == "single" || stream.match('""'))) { 712 end = true; 713 break; 714 } 715 if (!escaped && stream.match('``')) { 716 stringTokenizer = tokenCeylonString(type); 717 end = true; 718 break; 719 } 720 next = stream.next(); 721 escaped = type == "single" && !escaped && next == "\\"; 722 } 723 if (end) 724 state.tokenize = null; 725 return "string"; 726 } 727 } 728 729 def("text/x-ceylon", { 730 name: "clike", 731 keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" + 732 " exists extends finally for function given if import in interface is let module new" + 733 " nonempty object of out outer package return satisfies super switch then this throw" + 734 " try value void while"), 735 types: function(word) { 736 // In Ceylon all identifiers that start with an uppercase are types 737 var first = word.charAt(0); 738 return (first === first.toUpperCase() && first !== first.toLowerCase()); 739 }, 740 blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"), 741 defKeywords: words("class dynamic function interface module object package value"), 742 builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" + 743 " native optional sealed see serializable shared suppressWarnings tagged throws variable"), 744 isPunctuationChar: /[\[\]{}\(\),;\:\.`]/, 745 isOperatorChar: /[+\-*&%=<>!?|^~:\/]/, 746 numberStart: /[\d#$]/, 747 number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i, 748 multiLineStrings: true, 749 typeFirstDefinitions: true, 750 atoms: words("true false null larger smaller equal empty finished"), 751 indentSwitch: false, 752 styleDefs: false, 753 hooks: { 754 "@": function(stream) { 755 stream.eatWhile(/[\w\$_]/); 756 return "meta"; 757 }, 758 '"': function(stream, state) { 759 state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single"); 760 return state.tokenize(stream, state); 761 }, 762 '`': function(stream, state) { 763 if (!stringTokenizer || !stream.match('`')) return false; 764 state.tokenize = stringTokenizer; 765 stringTokenizer = null; 766 return state.tokenize(stream, state); 767 }, 768 "'": function(stream) { 769 stream.eatWhile(/[\w\$_\xa1-\uffff]/); 770 return "atom"; 771 }, 772 token: function(_stream, state, style) { 773 if ((style == "variable" || style == "variable-3") && 774 state.prevToken == ".") { 775 return "variable-2"; 776 } 777 } 778 }, 779 modeProps: { 780 fold: ["brace", "import"], 781 closeBrackets: {triples: '"'} 782 } 783 }); 784 785 }); -
src/wp-includes/js/codemirror/mode/css/css.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineMode("css", function(config, parserConfig) { 15 var inline = parserConfig.inline 16 if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); 17 18 var indentUnit = config.indentUnit, 19 tokenHooks = parserConfig.tokenHooks, 20 documentTypes = parserConfig.documentTypes || {}, 21 mediaTypes = parserConfig.mediaTypes || {}, 22 mediaFeatures = parserConfig.mediaFeatures || {}, 23 mediaValueKeywords = parserConfig.mediaValueKeywords || {}, 24 propertyKeywords = parserConfig.propertyKeywords || {}, 25 nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {}, 26 fontProperties = parserConfig.fontProperties || {}, 27 counterDescriptors = parserConfig.counterDescriptors || {}, 28 colorKeywords = parserConfig.colorKeywords || {}, 29 valueKeywords = parserConfig.valueKeywords || {}, 30 allowNested = parserConfig.allowNested, 31 supportsAtComponent = parserConfig.supportsAtComponent === true; 32 33 var type, override; 34 function ret(style, tp) { type = tp; return style; } 35 36 // Tokenizers 37 38 function tokenBase(stream, state) { 39 var ch = stream.next(); 40 if (tokenHooks[ch]) { 41 var result = tokenHooks[ch](stream, state); 42 if (result !== false) return result; 43 } 44 if (ch == "@") { 45 stream.eatWhile(/[\w\\\-]/); 46 return ret("def", stream.current()); 47 } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) { 48 return ret(null, "compare"); 49 } else if (ch == "\"" || ch == "'") { 50 state.tokenize = tokenString(ch); 51 return state.tokenize(stream, state); 52 } else if (ch == "#") { 53 stream.eatWhile(/[\w\\\-]/); 54 return ret("atom", "hash"); 55 } else if (ch == "!") { 56 stream.match(/^\s*\w*/); 57 return ret("keyword", "important"); 58 } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { 59 stream.eatWhile(/[\w.%]/); 60 return ret("number", "unit"); 61 } else if (ch === "-") { 62 if (/[\d.]/.test(stream.peek())) { 63 stream.eatWhile(/[\w.%]/); 64 return ret("number", "unit"); 65 } else if (stream.match(/^-[\w\\\-]+/)) { 66 stream.eatWhile(/[\w\\\-]/); 67 if (stream.match(/^\s*:/, false)) 68 return ret("variable-2", "variable-definition"); 69 return ret("variable-2", "variable"); 70 } else if (stream.match(/^\w+-/)) { 71 return ret("meta", "meta"); 72 } 73 } else if (/[,+>*\/]/.test(ch)) { 74 return ret(null, "select-op"); 75 } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { 76 return ret("qualifier", "qualifier"); 77 } else if (/[:;{}\[\]\(\)]/.test(ch)) { 78 return ret(null, ch); 79 } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) || 80 (ch == "d" && stream.match("omain(")) || 81 (ch == "r" && stream.match("egexp("))) { 82 stream.backUp(1); 83 state.tokenize = tokenParenthesized; 84 return ret("property", "word"); 85 } else if (/[\w\\\-]/.test(ch)) { 86 stream.eatWhile(/[\w\\\-]/); 87 return ret("property", "word"); 88 } else { 89 return ret(null, null); 90 } 91 } 92 93 function tokenString(quote) { 94 return function(stream, state) { 95 var escaped = false, ch; 96 while ((ch = stream.next()) != null) { 97 if (ch == quote && !escaped) { 98 if (quote == ")") stream.backUp(1); 99 break; 100 } 101 escaped = !escaped && ch == "\\"; 102 } 103 if (ch == quote || !escaped && quote != ")") state.tokenize = null; 104 return ret("string", "string"); 105 }; 106 } 107 108 function tokenParenthesized(stream, state) { 109 stream.next(); // Must be '(' 110 if (!stream.match(/\s*[\"\')]/, false)) 111 state.tokenize = tokenString(")"); 112 else 113 state.tokenize = null; 114 return ret(null, "("); 115 } 116 117 // Context management 118 119 function Context(type, indent, prev) { 120 this.type = type; 121 this.indent = indent; 122 this.prev = prev; 123 } 124 125 function pushContext(state, stream, type, indent) { 126 state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context); 127 return type; 128 } 129 130 function popContext(state) { 131 if (state.context.prev) 132 state.context = state.context.prev; 133 return state.context.type; 134 } 135 136 function pass(type, stream, state) { 137 return states[state.context.type](type, stream, state); 138 } 139 function popAndPass(type, stream, state, n) { 140 for (var i = n || 1; i > 0; i--) 141 state.context = state.context.prev; 142 return pass(type, stream, state); 143 } 144 145 // Parser 146 147 function wordAsValue(stream) { 148 var word = stream.current().toLowerCase(); 149 if (valueKeywords.hasOwnProperty(word)) 150 override = "atom"; 151 else if (colorKeywords.hasOwnProperty(word)) 152 override = "keyword"; 153 else 154 override = "variable"; 155 } 156 157 var states = {}; 158 159 states.top = function(type, stream, state) { 160 if (type == "{") { 161 return pushContext(state, stream, "block"); 162 } else if (type == "}" && state.context.prev) { 163 return popContext(state); 164 } else if (supportsAtComponent && /@component/.test(type)) { 165 return pushContext(state, stream, "atComponentBlock"); 166 } else if (/^@(-moz-)?document$/.test(type)) { 167 return pushContext(state, stream, "documentTypes"); 168 } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) { 169 return pushContext(state, stream, "atBlock"); 170 } else if (/^@(font-face|counter-style)/.test(type)) { 171 state.stateArg = type; 172 return "restricted_atBlock_before"; 173 } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) { 174 return "keyframes"; 175 } else if (type && type.charAt(0) == "@") { 176 return pushContext(state, stream, "at"); 177 } else if (type == "hash") { 178 override = "builtin"; 179 } else if (type == "word") { 180 override = "tag"; 181 } else if (type == "variable-definition") { 182 return "maybeprop"; 183 } else if (type == "interpolation") { 184 return pushContext(state, stream, "interpolation"); 185 } else if (type == ":") { 186 return "pseudo"; 187 } else if (allowNested && type == "(") { 188 return pushContext(state, stream, "parens"); 189 } 190 return state.context.type; 191 }; 192 193 states.block = function(type, stream, state) { 194 if (type == "word") { 195 var word = stream.current().toLowerCase(); 196 if (propertyKeywords.hasOwnProperty(word)) { 197 override = "property"; 198 return "maybeprop"; 199 } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) { 200 override = "string-2"; 201 return "maybeprop"; 202 } else if (allowNested) { 203 override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag"; 204 return "block"; 205 } else { 206 override += " error"; 207 return "maybeprop"; 208 } 209 } else if (type == "meta") { 210 return "block"; 211 } else if (!allowNested && (type == "hash" || type == "qualifier")) { 212 override = "error"; 213 return "block"; 214 } else { 215 return states.top(type, stream, state); 216 } 217 }; 218 219 states.maybeprop = function(type, stream, state) { 220 if (type == ":") return pushContext(state, stream, "prop"); 221 return pass(type, stream, state); 222 }; 223 224 states.prop = function(type, stream, state) { 225 if (type == ";") return popContext(state); 226 if (type == "{" && allowNested) return pushContext(state, stream, "propBlock"); 227 if (type == "}" || type == "{") return popAndPass(type, stream, state); 228 if (type == "(") return pushContext(state, stream, "parens"); 229 230 if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) { 231 override += " error"; 232 } else if (type == "word") { 233 wordAsValue(stream); 234 } else if (type == "interpolation") { 235 return pushContext(state, stream, "interpolation"); 236 } 237 return "prop"; 238 }; 239 240 states.propBlock = function(type, _stream, state) { 241 if (type == "}") return popContext(state); 242 if (type == "word") { override = "property"; return "maybeprop"; } 243 return state.context.type; 244 }; 245 246 states.parens = function(type, stream, state) { 247 if (type == "{" || type == "}") return popAndPass(type, stream, state); 248 if (type == ")") return popContext(state); 249 if (type == "(") return pushContext(state, stream, "parens"); 250 if (type == "interpolation") return pushContext(state, stream, "interpolation"); 251 if (type == "word") wordAsValue(stream); 252 return "parens"; 253 }; 254 255 states.pseudo = function(type, stream, state) { 256 if (type == "word") { 257 override = "variable-3"; 258 return state.context.type; 259 } 260 return pass(type, stream, state); 261 }; 262 263 states.documentTypes = function(type, stream, state) { 264 if (type == "word" && documentTypes.hasOwnProperty(stream.current())) { 265 override = "tag"; 266 return state.context.type; 267 } else { 268 return states.atBlock(type, stream, state); 269 } 270 }; 271 272 states.atBlock = function(type, stream, state) { 273 if (type == "(") return pushContext(state, stream, "atBlock_parens"); 274 if (type == "}" || type == ";") return popAndPass(type, stream, state); 275 if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top"); 276 277 if (type == "interpolation") return pushContext(state, stream, "interpolation"); 278 279 if (type == "word") { 280 var word = stream.current().toLowerCase(); 281 if (word == "only" || word == "not" || word == "and" || word == "or") 282 override = "keyword"; 283 else if (mediaTypes.hasOwnProperty(word)) 284 override = "attribute"; 285 else if (mediaFeatures.hasOwnProperty(word)) 286 override = "property"; 287 else if (mediaValueKeywords.hasOwnProperty(word)) 288 override = "keyword"; 289 else if (propertyKeywords.hasOwnProperty(word)) 290 override = "property"; 291 else if (nonStandardPropertyKeywords.hasOwnProperty(word)) 292 override = "string-2"; 293 else if (valueKeywords.hasOwnProperty(word)) 294 override = "atom"; 295 else if (colorKeywords.hasOwnProperty(word)) 296 override = "keyword"; 297 else 298 override = "error"; 299 } 300 return state.context.type; 301 }; 302 303 states.atComponentBlock = function(type, stream, state) { 304 if (type == "}") 305 return popAndPass(type, stream, state); 306 if (type == "{") 307 return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false); 308 if (type == "word") 309 override = "error"; 310 return state.context.type; 311 }; 312 313 states.atBlock_parens = function(type, stream, state) { 314 if (type == ")") return popContext(state); 315 if (type == "{" || type == "}") return popAndPass(type, stream, state, 2); 316 return states.atBlock(type, stream, state); 317 }; 318 319 states.restricted_atBlock_before = function(type, stream, state) { 320 if (type == "{") 321 return pushContext(state, stream, "restricted_atBlock"); 322 if (type == "word" && state.stateArg == "@counter-style") { 323 override = "variable"; 324 return "restricted_atBlock_before"; 325 } 326 return pass(type, stream, state); 327 }; 328 329 states.restricted_atBlock = function(type, stream, state) { 330 if (type == "}") { 331 state.stateArg = null; 332 return popContext(state); 333 } 334 if (type == "word") { 335 if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) || 336 (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase()))) 337 override = "error"; 338 else 339 override = "property"; 340 return "maybeprop"; 341 } 342 return "restricted_atBlock"; 343 }; 344 345 states.keyframes = function(type, stream, state) { 346 if (type == "word") { override = "variable"; return "keyframes"; } 347 if (type == "{") return pushContext(state, stream, "top"); 348 return pass(type, stream, state); 349 }; 350 351 states.at = function(type, stream, state) { 352 if (type == ";") return popContext(state); 353 if (type == "{" || type == "}") return popAndPass(type, stream, state); 354 if (type == "word") override = "tag"; 355 else if (type == "hash") override = "builtin"; 356 return "at"; 357 }; 358 359 states.interpolation = function(type, stream, state) { 360 if (type == "}") return popContext(state); 361 if (type == "{" || type == ";") return popAndPass(type, stream, state); 362 if (type == "word") override = "variable"; 363 else if (type != "variable" && type != "(" && type != ")") override = "error"; 364 return "interpolation"; 365 }; 366 367 return { 368 startState: function(base) { 369 return {tokenize: null, 370 state: inline ? "block" : "top", 371 stateArg: null, 372 context: new Context(inline ? "block" : "top", base || 0, null)}; 373 }, 374 375 token: function(stream, state) { 376 if (!state.tokenize && stream.eatSpace()) return null; 377 var style = (state.tokenize || tokenBase)(stream, state); 378 if (style && typeof style == "object") { 379 type = style[1]; 380 style = style[0]; 381 } 382 override = style; 383 state.state = states[state.state](type, stream, state); 384 return override; 385 }, 386 387 indent: function(state, textAfter) { 388 var cx = state.context, ch = textAfter && textAfter.charAt(0); 389 var indent = cx.indent; 390 if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev; 391 if (cx.prev) { 392 if (ch == "}" && (cx.type == "block" || cx.type == "top" || 393 cx.type == "interpolation" || cx.type == "restricted_atBlock")) { 394 // Resume indentation from parent context. 395 cx = cx.prev; 396 indent = cx.indent; 397 } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") || 398 ch == "{" && (cx.type == "at" || cx.type == "atBlock")) { 399 // Dedent relative to current context. 400 indent = Math.max(0, cx.indent - indentUnit); 401 cx = cx.prev; 402 } 403 } 404 return indent; 405 }, 406 407 electricChars: "}", 408 blockCommentStart: "/*", 409 blockCommentEnd: "*/", 410 fold: "brace" 411 }; 412 }); 413 414 function keySet(array) { 415 var keys = {}; 416 for (var i = 0; i < array.length; ++i) { 417 keys[array[i].toLowerCase()] = true; 418 } 419 return keys; 420 } 421 422 var documentTypes_ = [ 423 "domain", "regexp", "url", "url-prefix" 424 ], documentTypes = keySet(documentTypes_); 425 426 var mediaTypes_ = [ 427 "all", "aural", "braille", "handheld", "print", "projection", "screen", 428 "tty", "tv", "embossed" 429 ], mediaTypes = keySet(mediaTypes_); 430 431 var mediaFeatures_ = [ 432 "width", "min-width", "max-width", "height", "min-height", "max-height", 433 "device-width", "min-device-width", "max-device-width", "device-height", 434 "min-device-height", "max-device-height", "aspect-ratio", 435 "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", 436 "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", 437 "max-color", "color-index", "min-color-index", "max-color-index", 438 "monochrome", "min-monochrome", "max-monochrome", "resolution", 439 "min-resolution", "max-resolution", "scan", "grid", "orientation", 440 "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio", 441 "pointer", "any-pointer", "hover", "any-hover" 442 ], mediaFeatures = keySet(mediaFeatures_); 443 444 var mediaValueKeywords_ = [ 445 "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover", 446 "interlace", "progressive" 447 ], mediaValueKeywords = keySet(mediaValueKeywords_); 448 449 var propertyKeywords_ = [ 450 "align-content", "align-items", "align-self", "alignment-adjust", 451 "alignment-baseline", "anchor-point", "animation", "animation-delay", 452 "animation-direction", "animation-duration", "animation-fill-mode", 453 "animation-iteration-count", "animation-name", "animation-play-state", 454 "animation-timing-function", "appearance", "azimuth", "backface-visibility", 455 "background", "background-attachment", "background-blend-mode", "background-clip", 456 "background-color", "background-image", "background-origin", "background-position", 457 "background-repeat", "background-size", "baseline-shift", "binding", 458 "bleed", "bookmark-label", "bookmark-level", "bookmark-state", 459 "bookmark-target", "border", "border-bottom", "border-bottom-color", 460 "border-bottom-left-radius", "border-bottom-right-radius", 461 "border-bottom-style", "border-bottom-width", "border-collapse", 462 "border-color", "border-image", "border-image-outset", 463 "border-image-repeat", "border-image-slice", "border-image-source", 464 "border-image-width", "border-left", "border-left-color", 465 "border-left-style", "border-left-width", "border-radius", "border-right", 466 "border-right-color", "border-right-style", "border-right-width", 467 "border-spacing", "border-style", "border-top", "border-top-color", 468 "border-top-left-radius", "border-top-right-radius", "border-top-style", 469 "border-top-width", "border-width", "bottom", "box-decoration-break", 470 "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", 471 "caption-side", "clear", "clip", "color", "color-profile", "column-count", 472 "column-fill", "column-gap", "column-rule", "column-rule-color", 473 "column-rule-style", "column-rule-width", "column-span", "column-width", 474 "columns", "content", "counter-increment", "counter-reset", "crop", "cue", 475 "cue-after", "cue-before", "cursor", "direction", "display", 476 "dominant-baseline", "drop-initial-after-adjust", 477 "drop-initial-after-align", "drop-initial-before-adjust", 478 "drop-initial-before-align", "drop-initial-size", "drop-initial-value", 479 "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", 480 "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", 481 "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", 482 "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", 483 "font-stretch", "font-style", "font-synthesis", "font-variant", 484 "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", 485 "font-variant-ligatures", "font-variant-numeric", "font-variant-position", 486 "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", 487 "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap", 488 "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", 489 "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns", 490 "grid-template-rows", "hanging-punctuation", "height", "hyphens", 491 "icon", "image-orientation", "image-rendering", "image-resolution", 492 "inline-box-align", "justify-content", "left", "letter-spacing", 493 "line-break", "line-height", "line-stacking", "line-stacking-ruby", 494 "line-stacking-shift", "line-stacking-strategy", "list-style", 495 "list-style-image", "list-style-position", "list-style-type", "margin", 496 "margin-bottom", "margin-left", "margin-right", "margin-top", 497 "marks", "marquee-direction", "marquee-loop", 498 "marquee-play-count", "marquee-speed", "marquee-style", "max-height", 499 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", 500 "nav-left", "nav-right", "nav-up", "object-fit", "object-position", 501 "opacity", "order", "orphans", "outline", 502 "outline-color", "outline-offset", "outline-style", "outline-width", 503 "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", 504 "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", 505 "page", "page-break-after", "page-break-before", "page-break-inside", 506 "page-policy", "pause", "pause-after", "pause-before", "perspective", 507 "perspective-origin", "pitch", "pitch-range", "play-during", "position", 508 "presentation-level", "punctuation-trim", "quotes", "region-break-after", 509 "region-break-before", "region-break-inside", "region-fragment", 510 "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", 511 "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", 512 "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin", 513 "shape-outside", "size", "speak", "speak-as", "speak-header", 514 "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", 515 "tab-size", "table-layout", "target", "target-name", "target-new", 516 "target-position", "text-align", "text-align-last", "text-decoration", 517 "text-decoration-color", "text-decoration-line", "text-decoration-skip", 518 "text-decoration-style", "text-emphasis", "text-emphasis-color", 519 "text-emphasis-position", "text-emphasis-style", "text-height", 520 "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", 521 "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", 522 "text-wrap", "top", "transform", "transform-origin", "transform-style", 523 "transition", "transition-delay", "transition-duration", 524 "transition-property", "transition-timing-function", "unicode-bidi", 525 "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration", 526 "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", 527 "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break", 528 "word-spacing", "word-wrap", "z-index", 529 // SVG-specific 530 "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", 531 "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", 532 "color-interpolation", "color-interpolation-filters", 533 "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", 534 "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke", 535 "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", 536 "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", 537 "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", 538 "glyph-orientation-vertical", "text-anchor", "writing-mode" 539 ], propertyKeywords = keySet(propertyKeywords_); 540 541 var nonStandardPropertyKeywords_ = [ 542 "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color", 543 "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color", 544 "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside", 545 "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", 546 "searchfield-results-decoration", "zoom" 547 ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_); 548 549 var fontProperties_ = [ 550 "font-family", "src", "unicode-range", "font-variant", "font-feature-settings", 551 "font-stretch", "font-weight", "font-style" 552 ], fontProperties = keySet(fontProperties_); 553 554 var counterDescriptors_ = [ 555 "additive-symbols", "fallback", "negative", "pad", "prefix", "range", 556 "speak-as", "suffix", "symbols", "system" 557 ], counterDescriptors = keySet(counterDescriptors_); 558 559 var colorKeywords_ = [ 560 "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", 561 "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", 562 "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", 563 "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", 564 "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", 565 "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", 566 "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", 567 "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", 568 "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", 569 "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", 570 "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", 571 "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", 572 "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink", 573 "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", 574 "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", 575 "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", 576 "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", 577 "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", 578 "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", 579 "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", 580 "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", 581 "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", 582 "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", 583 "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", 584 "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", 585 "whitesmoke", "yellow", "yellowgreen" 586 ], colorKeywords = keySet(colorKeywords_); 587 588 var valueKeywords_ = [ 589 "above", "absolute", "activeborder", "additive", "activecaption", "afar", 590 "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate", 591 "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", 592 "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page", 593 "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary", 594 "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", 595 "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel", 596 "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian", 597 "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", 598 "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch", 599 "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", 600 "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse", 601 "compact", "condensed", "contain", "content", "contents", 602 "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop", 603 "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal", 604 "decimal-leading-zero", "default", "default-button", "dense", "destination-atop", 605 "destination-in", "destination-out", "destination-over", "devanagari", "difference", 606 "disc", "discard", "disclosure-closed", "disclosure-open", "document", 607 "dot-dash", "dot-dot-dash", 608 "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", 609 "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", 610 "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", 611 "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", 612 "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", 613 "ethiopic-halehame-gez", "ethiopic-halehame-om-et", 614 "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", 615 "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", 616 "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed", 617 "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes", 618 "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove", 619 "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew", 620 "help", "hidden", "hide", "higher", "highlight", "highlighttext", 621 "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore", 622 "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", 623 "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", 624 "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert", 625 "italic", "japanese-formal", "japanese-informal", "justify", "kannada", 626 "katakana", "katakana-iroha", "keep-all", "khmer", 627 "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal", 628 "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten", 629 "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem", 630 "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", 631 "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", 632 "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d", 633 "media-controls-background", "media-current-time-display", 634 "media-fullscreen-button", "media-mute-button", "media-play-button", 635 "media-return-to-realtime-button", "media-rewind-button", 636 "media-seek-back-button", "media-seek-forward-button", "media-slider", 637 "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", 638 "media-volume-slider-container", "media-volume-sliderthumb", "medium", 639 "menu", "menulist", "menulist-button", "menulist-text", 640 "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", 641 "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize", 642 "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", 643 "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", 644 "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote", 645 "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", 646 "outside", "outside-shape", "overlay", "overline", "padding", "padding-box", 647 "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter", 648 "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", 649 "progress", "push-button", "radial-gradient", "radio", "read-only", 650 "read-write", "read-write-plaintext-only", "rectangle", "region", 651 "relative", "repeat", "repeating-linear-gradient", 652 "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse", 653 "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY", 654 "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running", 655 "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen", 656 "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield", 657 "searchfield-cancel-button", "searchfield-decoration", 658 "searchfield-results-button", "searchfield-results-decoration", 659 "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", 660 "simp-chinese-formal", "simp-chinese-informal", "single", 661 "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal", 662 "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", 663 "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali", 664 "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square", 665 "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub", 666 "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table", 667 "table-caption", "table-cell", "table-column", "table-column-group", 668 "table-footer-group", "table-header-group", "table-row", "table-row-group", 669 "tamil", 670 "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", 671 "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", 672 "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", 673 "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", 674 "trad-chinese-formal", "trad-chinese-informal", "transform", 675 "translate", "translate3d", "translateX", "translateY", "translateZ", 676 "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up", 677 "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", 678 "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", 679 "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", 680 "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", 681 "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor", 682 "xx-large", "xx-small" 683 ], valueKeywords = keySet(valueKeywords_); 684 685 var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_) 686 .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_) 687 .concat(valueKeywords_); 688 CodeMirror.registerHelper("hintWords", "css", allWords); 689 690 function tokenCComment(stream, state) { 691 var maybeEnd = false, ch; 692 while ((ch = stream.next()) != null) { 693 if (maybeEnd && ch == "/") { 694 state.tokenize = null; 695 break; 696 } 697 maybeEnd = (ch == "*"); 698 } 699 return ["comment", "comment"]; 700 } 701 702 CodeMirror.defineMIME("text/css", { 703 documentTypes: documentTypes, 704 mediaTypes: mediaTypes, 705 mediaFeatures: mediaFeatures, 706 mediaValueKeywords: mediaValueKeywords, 707 propertyKeywords: propertyKeywords, 708 nonStandardPropertyKeywords: nonStandardPropertyKeywords, 709 fontProperties: fontProperties, 710 counterDescriptors: counterDescriptors, 711 colorKeywords: colorKeywords, 712 valueKeywords: valueKeywords, 713 tokenHooks: { 714 "/": function(stream, state) { 715 if (!stream.eat("*")) return false; 716 state.tokenize = tokenCComment; 717 return tokenCComment(stream, state); 718 } 719 }, 720 name: "css" 721 }); 722 723 CodeMirror.defineMIME("text/x-scss", { 724 mediaTypes: mediaTypes, 725 mediaFeatures: mediaFeatures, 726 mediaValueKeywords: mediaValueKeywords, 727 propertyKeywords: propertyKeywords, 728 nonStandardPropertyKeywords: nonStandardPropertyKeywords, 729 colorKeywords: colorKeywords, 730 valueKeywords: valueKeywords, 731 fontProperties: fontProperties, 732 allowNested: true, 733 tokenHooks: { 734 "/": function(stream, state) { 735 if (stream.eat("/")) { 736 stream.skipToEnd(); 737 return ["comment", "comment"]; 738 } else if (stream.eat("*")) { 739 state.tokenize = tokenCComment; 740 return tokenCComment(stream, state); 741 } else { 742 return ["operator", "operator"]; 743 } 744 }, 745 ":": function(stream) { 746 if (stream.match(/\s*\{/)) 747 return [null, "{"]; 748 return false; 749 }, 750 "$": function(stream) { 751 stream.match(/^[\w-]+/); 752 if (stream.match(/^\s*:/, false)) 753 return ["variable-2", "variable-definition"]; 754 return ["variable-2", "variable"]; 755 }, 756 "#": function(stream) { 757 if (!stream.eat("{")) return false; 758 return [null, "interpolation"]; 759 } 760 }, 761 name: "css", 762 helperType: "scss" 763 }); 764 765 CodeMirror.defineMIME("text/x-less", { 766 mediaTypes: mediaTypes, 767 mediaFeatures: mediaFeatures, 768 mediaValueKeywords: mediaValueKeywords, 769 propertyKeywords: propertyKeywords, 770 nonStandardPropertyKeywords: nonStandardPropertyKeywords, 771 colorKeywords: colorKeywords, 772 valueKeywords: valueKeywords, 773 fontProperties: fontProperties, 774 allowNested: true, 775 tokenHooks: { 776 "/": function(stream, state) { 777 if (stream.eat("/")) { 778 stream.skipToEnd(); 779 return ["comment", "comment"]; 780 } else if (stream.eat("*")) { 781 state.tokenize = tokenCComment; 782 return tokenCComment(stream, state); 783 } else { 784 return ["operator", "operator"]; 785 } 786 }, 787 "@": function(stream) { 788 if (stream.eat("{")) return [null, "interpolation"]; 789 if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false; 790 stream.eatWhile(/[\w\\\-]/); 791 if (stream.match(/^\s*:/, false)) 792 return ["variable-2", "variable-definition"]; 793 return ["variable-2", "variable"]; 794 }, 795 "&": function() { 796 return ["atom", "atom"]; 797 } 798 }, 799 name: "css", 800 helperType: "less" 801 }); 802 803 CodeMirror.defineMIME("text/x-gss", { 804 documentTypes: documentTypes, 805 mediaTypes: mediaTypes, 806 mediaFeatures: mediaFeatures, 807 propertyKeywords: propertyKeywords, 808 nonStandardPropertyKeywords: nonStandardPropertyKeywords, 809 fontProperties: fontProperties, 810 counterDescriptors: counterDescriptors, 811 colorKeywords: colorKeywords, 812 valueKeywords: valueKeywords, 813 supportsAtComponent: true, 814 tokenHooks: { 815 "/": function(stream, state) { 816 if (!stream.eat("*")) return false; 817 state.tokenize = tokenCComment; 818 return tokenCComment(stream, state); 819 } 820 }, 821 name: "css", 822 helperType: "gss" 823 }); 824 825 }); -
src/wp-includes/js/codemirror/mode/diff/diff.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineMode("diff", function() { 15 16 var TOKEN_NAMES = { 17 '+': 'positive', 18 '-': 'negative', 19 '@': 'meta' 20 }; 21 22 return { 23 token: function(stream) { 24 var tw_pos = stream.string.search(/[\t ]+?$/); 25 26 if (!stream.sol() || tw_pos === 0) { 27 stream.skipToEnd(); 28 return ("error " + ( 29 TOKEN_NAMES[stream.string.charAt(0)] || '')).replace(/ $/, ''); 30 } 31 32 var token_name = TOKEN_NAMES[stream.peek()] || stream.skipToEnd(); 33 34 if (tw_pos === -1) { 35 stream.skipToEnd(); 36 } else { 37 stream.pos = tw_pos; 38 } 39 40 return token_name; 41 } 42 }; 43 }); 44 45 CodeMirror.defineMIME("text/x-diff", "diff"); 46 47 }); -
src/wp-includes/js/codemirror/mode/htmlmixed/htmlmixed.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var defaultTags = { 15 script: [ 16 ["lang", /(javascript|babel)/i, "javascript"], 17 ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"], 18 ["type", /./, "text/plain"], 19 [null, null, "javascript"] 20 ], 21 style: [ 22 ["lang", /^css$/i, "css"], 23 ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"], 24 ["type", /./, "text/plain"], 25 [null, null, "css"] 26 ] 27 }; 28 29 function maybeBackup(stream, pat, style) { 30 var cur = stream.current(), close = cur.search(pat); 31 if (close > -1) { 32 stream.backUp(cur.length - close); 33 } else if (cur.match(/<\/?$/)) { 34 stream.backUp(cur.length); 35 if (!stream.match(pat, false)) stream.match(cur); 36 } 37 return style; 38 } 39 40 var attrRegexpCache = {}; 41 function getAttrRegexp(attr) { 42 var regexp = attrRegexpCache[attr]; 43 if (regexp) return regexp; 44 return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*"); 45 } 46 47 function getAttrValue(text, attr) { 48 var match = text.match(getAttrRegexp(attr)) 49 return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : "" 50 } 51 52 function getTagRegexp(tagName, anchored) { 53 return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i"); 54 } 55 56 function addTags(from, to) { 57 for (var tag in from) { 58 var dest = to[tag] || (to[tag] = []); 59 var source = from[tag]; 60 for (var i = source.length - 1; i >= 0; i--) 61 dest.unshift(source[i]) 62 } 63 } 64 65 function findMatchingMode(tagInfo, tagText) { 66 for (var i = 0; i < tagInfo.length; i++) { 67 var spec = tagInfo[i]; 68 if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2]; 69 } 70 } 71 72 CodeMirror.defineMode("htmlmixed", function (config, parserConfig) { 73 var htmlMode = CodeMirror.getMode(config, { 74 name: "xml", 75 htmlMode: true, 76 multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, 77 multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag 78 }); 79 80 var tags = {}; 81 var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes; 82 addTags(defaultTags, tags); 83 if (configTags) addTags(configTags, tags); 84 if (configScript) for (var i = configScript.length - 1; i >= 0; i--) 85 tags.script.unshift(["type", configScript[i].matches, configScript[i].mode]) 86 87 function html(stream, state) { 88 var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName 89 if (tag && !/[<>\s\/]/.test(stream.current()) && 90 (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) && 91 tags.hasOwnProperty(tagName)) { 92 state.inTag = tagName + " " 93 } else if (state.inTag && tag && />$/.test(stream.current())) { 94 var inTag = /^([\S]+) (.*)/.exec(state.inTag) 95 state.inTag = null 96 var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2]) 97 var mode = CodeMirror.getMode(config, modeSpec) 98 var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false); 99 state.token = function (stream, state) { 100 if (stream.match(endTagA, false)) { 101 state.token = html; 102 state.localState = state.localMode = null; 103 return null; 104 } 105 return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState)); 106 }; 107 state.localMode = mode; 108 state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "")); 109 } else if (state.inTag) { 110 state.inTag += stream.current() 111 if (stream.eol()) state.inTag += " " 112 } 113 return style; 114 }; 115 116 return { 117 startState: function () { 118 var state = CodeMirror.startState(htmlMode); 119 return {token: html, inTag: null, localMode: null, localState: null, htmlState: state}; 120 }, 121 122 copyState: function (state) { 123 var local; 124 if (state.localState) { 125 local = CodeMirror.copyState(state.localMode, state.localState); 126 } 127 return {token: state.token, inTag: state.inTag, 128 localMode: state.localMode, localState: local, 129 htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; 130 }, 131 132 token: function (stream, state) { 133 return state.token(stream, state); 134 }, 135 136 indent: function (state, textAfter) { 137 if (!state.localMode || /^\s*<\//.test(textAfter)) 138 return htmlMode.indent(state.htmlState, textAfter); 139 else if (state.localMode.indent) 140 return state.localMode.indent(state.localState, textAfter); 141 else 142 return CodeMirror.Pass; 143 }, 144 145 innerMode: function (state) { 146 return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; 147 } 148 }; 149 }, "xml", "javascript", "css"); 150 151 CodeMirror.defineMIME("text/html", "htmlmixed"); 152 }); -
src/wp-includes/js/codemirror/mode/http/http.js
1 s// CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineMode("http", function() { 15 function failFirstLine(stream, state) { 16 stream.skipToEnd(); 17 state.cur = header; 18 return "error"; 19 } 20 21 function start(stream, state) { 22 if (stream.match(/^HTTP\/\d\.\d/)) { 23 state.cur = responseStatusCode; 24 return "keyword"; 25 } else if (stream.match(/^[A-Z]+/) && /[ \t]/.test(stream.peek())) { 26 state.cur = requestPath; 27 return "keyword"; 28 } else { 29 return failFirstLine(stream, state); 30 } 31 } 32 33 function responseStatusCode(stream, state) { 34 var code = stream.match(/^\d+/); 35 if (!code) return failFirstLine(stream, state); 36 37 state.cur = responseStatusText; 38 var status = Number(code[0]); 39 if (status >= 100 && status < 200) { 40 return "positive informational"; 41 } else if (status >= 200 && status < 300) { 42 return "positive success"; 43 } else if (status >= 300 && status < 400) { 44 return "positive redirect"; 45 } else if (status >= 400 && status < 500) { 46 return "negative client-error"; 47 } else if (status >= 500 && status < 600) { 48 return "negative server-error"; 49 } else { 50 return "error"; 51 } 52 } 53 54 function responseStatusText(stream, state) { 55 stream.skipToEnd(); 56 state.cur = header; 57 return null; 58 } 59 60 function requestPath(stream, state) { 61 stream.eatWhile(/\S/); 62 state.cur = requestProtocol; 63 return "string-2"; 64 } 65 66 function requestProtocol(stream, state) { 67 if (stream.match(/^HTTP\/\d\.\d$/)) { 68 state.cur = header; 69 return "keyword"; 70 } else { 71 return failFirstLine(stream, state); 72 } 73 } 74 75 function header(stream) { 76 if (stream.sol() && !stream.eat(/[ \t]/)) { 77 if (stream.match(/^.*?:/)) { 78 return "atom"; 79 } else { 80 stream.skipToEnd(); 81 return "error"; 82 } 83 } else { 84 stream.skipToEnd(); 85 return "string"; 86 } 87 } 88 89 function body(stream) { 90 stream.skipToEnd(); 91 return null; 92 } 93 94 return { 95 token: function(stream, state) { 96 var cur = state.cur; 97 if (cur != header && cur != body && stream.eatSpace()) return null; 98 return cur(stream, state); 99 }, 100 101 blankLine: function(state) { 102 state.cur = body; 103 }, 104 105 startState: function() { 106 return {cur: start}; 107 } 108 }; 109 }); 110 111 CodeMirror.defineMIME("message/http", "http"); 112 113 }); -
src/wp-includes/js/codemirror/mode/javascript/javascript.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 function expressionAllowed(stream, state, backUp) { 15 return /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) || 16 (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) 17 } 18 19 CodeMirror.defineMode("javascript", function(config, parserConfig) { 20 var indentUnit = config.indentUnit; 21 var statementIndent = parserConfig.statementIndent; 22 var jsonldMode = parserConfig.jsonld; 23 var jsonMode = parserConfig.json || jsonldMode; 24 var isTS = parserConfig.typescript; 25 var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; 26 27 // Tokenizer 28 29 var keywords = function(){ 30 function kw(type) {return {type: type, style: "keyword"};} 31 var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); 32 var operator = kw("operator"), atom = {type: "atom", style: "atom"}; 33 34 var jsKeywords = { 35 "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, 36 "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C, 37 "var": kw("var"), "const": kw("var"), "let": kw("var"), 38 "function": kw("function"), "catch": kw("catch"), 39 "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), 40 "in": operator, "typeof": operator, "instanceof": operator, 41 "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, 42 "this": kw("this"), "class": kw("class"), "super": kw("atom"), 43 "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, 44 "await": C, "async": kw("async") 45 }; 46 47 // Extend the 'normal' keywords with the TypeScript language extensions 48 if (isTS) { 49 var type = {type: "variable", style: "variable-3"}; 50 var tsKeywords = { 51 // object-like things 52 "interface": kw("class"), 53 "implements": C, 54 "namespace": C, 55 "module": kw("module"), 56 "enum": kw("module"), 57 "type": kw("type"), 58 59 // scope modifiers 60 "public": kw("modifier"), 61 "private": kw("modifier"), 62 "protected": kw("modifier"), 63 "abstract": kw("modifier"), 64 65 // operators 66 "as": operator, 67 68 // types 69 "string": type, "number": type, "boolean": type, "any": type 70 }; 71 72 for (var attr in tsKeywords) { 73 jsKeywords[attr] = tsKeywords[attr]; 74 } 75 } 76 77 return jsKeywords; 78 }(); 79 80 var isOperatorChar = /[+\-*&%=<>!?|~^]/; 81 var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; 82 83 function readRegexp(stream) { 84 var escaped = false, next, inSet = false; 85 while ((next = stream.next()) != null) { 86 if (!escaped) { 87 if (next == "/" && !inSet) return; 88 if (next == "[") inSet = true; 89 else if (inSet && next == "]") inSet = false; 90 } 91 escaped = !escaped && next == "\\"; 92 } 93 } 94 95 // Used as scratch variables to communicate multiple values without 96 // consing up tons of objects. 97 var type, content; 98 function ret(tp, style, cont) { 99 type = tp; content = cont; 100 return style; 101 } 102 function tokenBase(stream, state) { 103 var ch = stream.next(); 104 if (ch == '"' || ch == "'") { 105 state.tokenize = tokenString(ch); 106 return state.tokenize(stream, state); 107 } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { 108 return ret("number", "number"); 109 } else if (ch == "." && stream.match("..")) { 110 return ret("spread", "meta"); 111 } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { 112 return ret(ch); 113 } else if (ch == "=" && stream.eat(">")) { 114 return ret("=>", "operator"); 115 } else if (ch == "0" && stream.eat(/x/i)) { 116 stream.eatWhile(/[\da-f]/i); 117 return ret("number", "number"); 118 } else if (ch == "0" && stream.eat(/o/i)) { 119 stream.eatWhile(/[0-7]/i); 120 return ret("number", "number"); 121 } else if (ch == "0" && stream.eat(/b/i)) { 122 stream.eatWhile(/[01]/i); 123 return ret("number", "number"); 124 } else if (/\d/.test(ch)) { 125 stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); 126 return ret("number", "number"); 127 } else if (ch == "/") { 128 if (stream.eat("*")) { 129 state.tokenize = tokenComment; 130 return tokenComment(stream, state); 131 } else if (stream.eat("/")) { 132 stream.skipToEnd(); 133 return ret("comment", "comment"); 134 } else if (expressionAllowed(stream, state, 1)) { 135 readRegexp(stream); 136 stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); 137 return ret("regexp", "string-2"); 138 } else { 139 stream.eatWhile(isOperatorChar); 140 return ret("operator", "operator", stream.current()); 141 } 142 } else if (ch == "`") { 143 state.tokenize = tokenQuasi; 144 return tokenQuasi(stream, state); 145 } else if (ch == "#") { 146 stream.skipToEnd(); 147 return ret("error", "error"); 148 } else if (isOperatorChar.test(ch)) { 149 if (ch != ">" || !state.lexical || state.lexical.type != ">") 150 stream.eatWhile(isOperatorChar); 151 return ret("operator", "operator", stream.current()); 152 } else if (wordRE.test(ch)) { 153 stream.eatWhile(wordRE); 154 var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; 155 return (known && state.lastType != ".") ? ret(known.type, known.style, word) : 156 ret("variable", "variable", word); 157 } 158 } 159 160 function tokenString(quote) { 161 return function(stream, state) { 162 var escaped = false, next; 163 if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ 164 state.tokenize = tokenBase; 165 return ret("jsonld-keyword", "meta"); 166 } 167 while ((next = stream.next()) != null) { 168 if (next == quote && !escaped) break; 169 escaped = !escaped && next == "\\"; 170 } 171 if (!escaped) state.tokenize = tokenBase; 172 return ret("string", "string"); 173 }; 174 } 175 176 function tokenComment(stream, state) { 177 var maybeEnd = false, ch; 178 while (ch = stream.next()) { 179 if (ch == "/" && maybeEnd) { 180 state.tokenize = tokenBase; 181 break; 182 } 183 maybeEnd = (ch == "*"); 184 } 185 return ret("comment", "comment"); 186 } 187 188 function tokenQuasi(stream, state) { 189 var escaped = false, next; 190 while ((next = stream.next()) != null) { 191 if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { 192 state.tokenize = tokenBase; 193 break; 194 } 195 escaped = !escaped && next == "\\"; 196 } 197 return ret("quasi", "string-2", stream.current()); 198 } 199 200 var brackets = "([{}])"; 201 // This is a crude lookahead trick to try and notice that we're 202 // parsing the argument patterns for a fat-arrow function before we 203 // actually hit the arrow token. It only works if the arrow is on 204 // the same line as the arguments and there's no strange noise 205 // (comments) in between. Fallback is to only notice when we hit the 206 // arrow, and not declare the arguments as locals for the arrow 207 // body. 208 function findFatArrow(stream, state) { 209 if (state.fatArrowAt) state.fatArrowAt = null; 210 var arrow = stream.string.indexOf("=>", stream.start); 211 if (arrow < 0) return; 212 213 if (isTS) { // Try to skip TypeScript return type declarations after the arguments 214 var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) 215 if (m) arrow = m.index 216 } 217 218 var depth = 0, sawSomething = false; 219 for (var pos = arrow - 1; pos >= 0; --pos) { 220 var ch = stream.string.charAt(pos); 221 var bracket = brackets.indexOf(ch); 222 if (bracket >= 0 && bracket < 3) { 223 if (!depth) { ++pos; break; } 224 if (--depth == 0) { if (ch == "(") sawSomething = true; break; } 225 } else if (bracket >= 3 && bracket < 6) { 226 ++depth; 227 } else if (wordRE.test(ch)) { 228 sawSomething = true; 229 } else if (/["'\/]/.test(ch)) { 230 return; 231 } else if (sawSomething && !depth) { 232 ++pos; 233 break; 234 } 235 } 236 if (sawSomething && !depth) state.fatArrowAt = pos; 237 } 238 239 // Parser 240 241 var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; 242 243 function JSLexical(indented, column, type, align, prev, info) { 244 this.indented = indented; 245 this.column = column; 246 this.type = type; 247 this.prev = prev; 248 this.info = info; 249 if (align != null) this.align = align; 250 } 251 252 function inScope(state, varname) { 253 for (var v = state.localVars; v; v = v.next) 254 if (v.name == varname) return true; 255 for (var cx = state.context; cx; cx = cx.prev) { 256 for (var v = cx.vars; v; v = v.next) 257 if (v.name == varname) return true; 258 } 259 } 260 261 function parseJS(state, style, type, content, stream) { 262 var cc = state.cc; 263 // Communicate our context to the combinators. 264 // (Less wasteful than consing up a hundred closures on every call.) 265 cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; 266 267 if (!state.lexical.hasOwnProperty("align")) 268 state.lexical.align = true; 269 270 while(true) { 271 var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; 272 if (combinator(type, content)) { 273 while(cc.length && cc[cc.length - 1].lex) 274 cc.pop()(); 275 if (cx.marked) return cx.marked; 276 if (type == "variable" && inScope(state, content)) return "variable-2"; 277 return style; 278 } 279 } 280 } 281 282 // Combinator utils 283 284 var cx = {state: null, column: null, marked: null, cc: null}; 285 function pass() { 286 for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); 287 } 288 function cont() { 289 pass.apply(null, arguments); 290 return true; 291 } 292 function register(varname) { 293 function inList(list) { 294 for (var v = list; v; v = v.next) 295 if (v.name == varname) return true; 296 return false; 297 } 298 var state = cx.state; 299 cx.marked = "def"; 300 if (state.context) { 301 if (inList(state.localVars)) return; 302 state.localVars = {name: varname, next: state.localVars}; 303 } else { 304 if (inList(state.globalVars)) return; 305 if (parserConfig.globalVars) 306 state.globalVars = {name: varname, next: state.globalVars}; 307 } 308 } 309 310 // Combinators 311 312 var defaultVars = {name: "this", next: {name: "arguments"}}; 313 function pushcontext() { 314 cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; 315 cx.state.localVars = defaultVars; 316 } 317 function popcontext() { 318 cx.state.localVars = cx.state.context.vars; 319 cx.state.context = cx.state.context.prev; 320 } 321 function pushlex(type, info) { 322 var result = function() { 323 var state = cx.state, indent = state.indented; 324 if (state.lexical.type == "stat") indent = state.lexical.indented; 325 else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) 326 indent = outer.indented; 327 state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); 328 }; 329 result.lex = true; 330 return result; 331 } 332 function poplex() { 333 var state = cx.state; 334 if (state.lexical.prev) { 335 if (state.lexical.type == ")") 336 state.indented = state.lexical.indented; 337 state.lexical = state.lexical.prev; 338 } 339 } 340 poplex.lex = true; 341 342 function expect(wanted) { 343 function exp(type) { 344 if (type == wanted) return cont(); 345 else if (wanted == ";") return pass(); 346 else return cont(exp); 347 }; 348 return exp; 349 } 350 351 function statement(type, value) { 352 if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); 353 if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); 354 if (type == "keyword b") return cont(pushlex("form"), statement, poplex); 355 if (type == "{") return cont(pushlex("}"), block, poplex); 356 if (type == ";") return cont(); 357 if (type == "if") { 358 if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) 359 cx.state.cc.pop()(); 360 return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); 361 } 362 if (type == "function") return cont(functiondef); 363 if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); 364 if (type == "variable") return cont(pushlex("stat"), maybelabel); 365 if (type == "switch") return cont(pushlex("form"), parenExpr, pushlex("}", "switch"), expect("{"), 366 block, poplex, poplex); 367 if (type == "case") return cont(expression, expect(":")); 368 if (type == "default") return cont(expect(":")); 369 if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), 370 statement, poplex, popcontext); 371 if (type == "class") return cont(pushlex("form"), className, poplex); 372 if (type == "export") return cont(pushlex("stat"), afterExport, poplex); 373 if (type == "import") return cont(pushlex("stat"), afterImport, poplex); 374 if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex) 375 if (type == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";")); 376 if (type == "async") return cont(statement) 377 return pass(pushlex("stat"), expression, expect(";"), poplex); 378 } 379 function expression(type) { 380 return expressionInner(type, false); 381 } 382 function expressionNoComma(type) { 383 return expressionInner(type, true); 384 } 385 function parenExpr(type) { 386 if (type != "(") return pass() 387 return cont(pushlex(")"), expression, expect(")"), poplex) 388 } 389 function expressionInner(type, noComma) { 390 if (cx.state.fatArrowAt == cx.stream.start) { 391 var body = noComma ? arrowBodyNoComma : arrowBody; 392 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); 393 else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); 394 } 395 396 var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; 397 if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); 398 if (type == "function") return cont(functiondef, maybeop); 399 if (type == "class") return cont(pushlex("form"), classExpression, poplex); 400 if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression); 401 if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); 402 if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); 403 if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); 404 if (type == "{") return contCommasep(objprop, "}", null, maybeop); 405 if (type == "quasi") return pass(quasi, maybeop); 406 if (type == "new") return cont(maybeTarget(noComma)); 407 return cont(); 408 } 409 function maybeexpression(type) { 410 if (type.match(/[;\}\)\],]/)) return pass(); 411 return pass(expression); 412 } 413 function maybeexpressionNoComma(type) { 414 if (type.match(/[;\}\)\],]/)) return pass(); 415 return pass(expressionNoComma); 416 } 417 418 function maybeoperatorComma(type, value) { 419 if (type == ",") return cont(expression); 420 return maybeoperatorNoComma(type, value, false); 421 } 422 function maybeoperatorNoComma(type, value, noComma) { 423 var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; 424 var expr = noComma == false ? expression : expressionNoComma; 425 if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); 426 if (type == "operator") { 427 if (/\+\+|--/.test(value)) return cont(me); 428 if (value == "?") return cont(expression, expect(":"), expr); 429 return cont(expr); 430 } 431 if (type == "quasi") { return pass(quasi, me); } 432 if (type == ";") return; 433 if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); 434 if (type == ".") return cont(property, me); 435 if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); 436 } 437 function quasi(type, value) { 438 if (type != "quasi") return pass(); 439 if (value.slice(value.length - 2) != "${") return cont(quasi); 440 return cont(expression, continueQuasi); 441 } 442 function continueQuasi(type) { 443 if (type == "}") { 444 cx.marked = "string-2"; 445 cx.state.tokenize = tokenQuasi; 446 return cont(quasi); 447 } 448 } 449 function arrowBody(type) { 450 findFatArrow(cx.stream, cx.state); 451 return pass(type == "{" ? statement : expression); 452 } 453 function arrowBodyNoComma(type) { 454 findFatArrow(cx.stream, cx.state); 455 return pass(type == "{" ? statement : expressionNoComma); 456 } 457 function maybeTarget(noComma) { 458 return function(type) { 459 if (type == ".") return cont(noComma ? targetNoComma : target); 460 else return pass(noComma ? expressionNoComma : expression); 461 }; 462 } 463 function target(_, value) { 464 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } 465 } 466 function targetNoComma(_, value) { 467 if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } 468 } 469 function maybelabel(type) { 470 if (type == ":") return cont(poplex, statement); 471 return pass(maybeoperatorComma, expect(";"), poplex); 472 } 473 function property(type) { 474 if (type == "variable") {cx.marked = "property"; return cont();} 475 } 476 function objprop(type, value) { 477 if (type == "async") { 478 cx.marked = "property"; 479 return cont(objprop); 480 } else if (type == "variable" || cx.style == "keyword") { 481 cx.marked = "property"; 482 if (value == "get" || value == "set") return cont(getterSetter); 483 return cont(afterprop); 484 } else if (type == "number" || type == "string") { 485 cx.marked = jsonldMode ? "property" : (cx.style + " property"); 486 return cont(afterprop); 487 } else if (type == "jsonld-keyword") { 488 return cont(afterprop); 489 } else if (type == "modifier") { 490 return cont(objprop) 491 } else if (type == "[") { 492 return cont(expression, expect("]"), afterprop); 493 } else if (type == "spread") { 494 return cont(expression); 495 } else if (type == ":") { 496 return pass(afterprop) 497 } 498 } 499 function getterSetter(type) { 500 if (type != "variable") return pass(afterprop); 501 cx.marked = "property"; 502 return cont(functiondef); 503 } 504 function afterprop(type) { 505 if (type == ":") return cont(expressionNoComma); 506 if (type == "(") return pass(functiondef); 507 } 508 function commasep(what, end, sep) { 509 function proceed(type, value) { 510 if (sep ? sep.indexOf(type) > -1 : type == ",") { 511 var lex = cx.state.lexical; 512 if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; 513 return cont(function(type, value) { 514 if (type == end || value == end) return pass() 515 return pass(what) 516 }, proceed); 517 } 518 if (type == end || value == end) return cont(); 519 return cont(expect(end)); 520 } 521 return function(type, value) { 522 if (type == end || value == end) return cont(); 523 return pass(what, proceed); 524 }; 525 } 526 function contCommasep(what, end, info) { 527 for (var i = 3; i < arguments.length; i++) 528 cx.cc.push(arguments[i]); 529 return cont(pushlex(end, info), commasep(what, end), poplex); 530 } 531 function block(type) { 532 if (type == "}") return cont(); 533 return pass(statement, block); 534 } 535 function maybetype(type, value) { 536 if (isTS) { 537 if (type == ":") return cont(typeexpr); 538 if (value == "?") return cont(maybetype); 539 } 540 } 541 function typeexpr(type) { 542 if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} 543 if (type == "string" || type == "number" || type == "atom") return cont(afterType); 544 if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex) 545 if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) 546 } 547 function maybeReturnType(type) { 548 if (type == "=>") return cont(typeexpr) 549 } 550 function typeprop(type, value) { 551 if (type == "variable" || cx.style == "keyword") { 552 cx.marked = "property" 553 return cont(typeprop) 554 } else if (value == "?") { 555 return cont(typeprop) 556 } else if (type == ":") { 557 return cont(typeexpr) 558 } 559 } 560 function typearg(type) { 561 if (type == "variable") return cont(typearg) 562 else if (type == ":") return cont(typeexpr) 563 } 564 function afterType(type, value) { 565 if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) 566 if (value == "|" || type == ".") return cont(typeexpr) 567 if (type == "[") return cont(expect("]"), afterType) 568 } 569 function vardef() { 570 return pass(pattern, maybetype, maybeAssign, vardefCont); 571 } 572 function pattern(type, value) { 573 if (type == "modifier") return cont(pattern) 574 if (type == "variable") { register(value); return cont(); } 575 if (type == "spread") return cont(pattern); 576 if (type == "[") return contCommasep(pattern, "]"); 577 if (type == "{") return contCommasep(proppattern, "}"); 578 } 579 function proppattern(type, value) { 580 if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { 581 register(value); 582 return cont(maybeAssign); 583 } 584 if (type == "variable") cx.marked = "property"; 585 if (type == "spread") return cont(pattern); 586 if (type == "}") return pass(); 587 return cont(expect(":"), pattern, maybeAssign); 588 } 589 function maybeAssign(_type, value) { 590 if (value == "=") return cont(expressionNoComma); 591 } 592 function vardefCont(type) { 593 if (type == ",") return cont(vardef); 594 } 595 function maybeelse(type, value) { 596 if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); 597 } 598 function forspec(type) { 599 if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); 600 } 601 function forspec1(type) { 602 if (type == "var") return cont(vardef, expect(";"), forspec2); 603 if (type == ";") return cont(forspec2); 604 if (type == "variable") return cont(formaybeinof); 605 return pass(expression, expect(";"), forspec2); 606 } 607 function formaybeinof(_type, value) { 608 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } 609 return cont(maybeoperatorComma, forspec2); 610 } 611 function forspec2(type, value) { 612 if (type == ";") return cont(forspec3); 613 if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } 614 return pass(expression, expect(";"), forspec3); 615 } 616 function forspec3(type) { 617 if (type != ")") cont(expression); 618 } 619 function functiondef(type, value) { 620 if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} 621 if (type == "variable") {register(value); return cont(functiondef);} 622 if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); 623 } 624 function funarg(type) { 625 if (type == "spread") return cont(funarg); 626 return pass(pattern, maybetype, maybeAssign); 627 } 628 function classExpression(type, value) { 629 // Class expressions may have an optional name. 630 if (type == "variable") return className(type, value); 631 return classNameAfter(type, value); 632 } 633 function className(type, value) { 634 if (type == "variable") {register(value); return cont(classNameAfter);} 635 } 636 function classNameAfter(type, value) { 637 if (value == "extends" || value == "implements") return cont(isTS ? typeexpr : expression, classNameAfter); 638 if (type == "{") return cont(pushlex("}"), classBody, poplex); 639 } 640 function classBody(type, value) { 641 if (type == "variable" || cx.style == "keyword") { 642 if ((value == "static" || value == "get" || value == "set" || 643 (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) && 644 cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) { 645 cx.marked = "keyword"; 646 return cont(classBody); 647 } 648 cx.marked = "property"; 649 return cont(isTS ? classfield : functiondef, classBody); 650 } 651 if (value == "*") { 652 cx.marked = "keyword"; 653 return cont(classBody); 654 } 655 if (type == ";") return cont(classBody); 656 if (type == "}") return cont(); 657 } 658 function classfield(type, value) { 659 if (value == "?") return cont(classfield) 660 if (type == ":") return cont(typeexpr, maybeAssign) 661 return pass(functiondef) 662 } 663 function afterExport(type, value) { 664 if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } 665 if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } 666 if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); 667 return pass(statement); 668 } 669 function exportField(type, value) { 670 if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } 671 if (type == "variable") return pass(expressionNoComma, exportField); 672 } 673 function afterImport(type) { 674 if (type == "string") return cont(); 675 return pass(importSpec, maybeMoreImports, maybeFrom); 676 } 677 function importSpec(type, value) { 678 if (type == "{") return contCommasep(importSpec, "}"); 679 if (type == "variable") register(value); 680 if (value == "*") cx.marked = "keyword"; 681 return cont(maybeAs); 682 } 683 function maybeMoreImports(type) { 684 if (type == ",") return cont(importSpec, maybeMoreImports) 685 } 686 function maybeAs(_type, value) { 687 if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } 688 } 689 function maybeFrom(_type, value) { 690 if (value == "from") { cx.marked = "keyword"; return cont(expression); } 691 } 692 function arrayLiteral(type) { 693 if (type == "]") return cont(); 694 return pass(commasep(expressionNoComma, "]")); 695 } 696 697 function isContinuedStatement(state, textAfter) { 698 return state.lastType == "operator" || state.lastType == "," || 699 isOperatorChar.test(textAfter.charAt(0)) || 700 /[,.]/.test(textAfter.charAt(0)); 701 } 702 703 // Interface 704 705 return { 706 startState: function(basecolumn) { 707 var state = { 708 tokenize: tokenBase, 709 lastType: "sof", 710 cc: [], 711 lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), 712 localVars: parserConfig.localVars, 713 context: parserConfig.localVars && {vars: parserConfig.localVars}, 714 indented: basecolumn || 0 715 }; 716 if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") 717 state.globalVars = parserConfig.globalVars; 718 return state; 719 }, 720 721 token: function(stream, state) { 722 if (stream.sol()) { 723 if (!state.lexical.hasOwnProperty("align")) 724 state.lexical.align = false; 725 state.indented = stream.indentation(); 726 findFatArrow(stream, state); 727 } 728 if (state.tokenize != tokenComment && stream.eatSpace()) return null; 729 var style = state.tokenize(stream, state); 730 if (type == "comment") return style; 731 state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; 732 return parseJS(state, style, type, content, stream); 733 }, 734 735 indent: function(state, textAfter) { 736 if (state.tokenize == tokenComment) return CodeMirror.Pass; 737 if (state.tokenize != tokenBase) return 0; 738 var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top 739 // Kludge to prevent 'maybelse' from blocking lexical scope pops 740 if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { 741 var c = state.cc[i]; 742 if (c == poplex) lexical = lexical.prev; 743 else if (c != maybeelse) break; 744 } 745 while ((lexical.type == "stat" || lexical.type == "form") && 746 (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && 747 (top == maybeoperatorComma || top == maybeoperatorNoComma) && 748 !/^[,\.=+\-*:?[\(]/.test(textAfter)))) 749 lexical = lexical.prev; 750 if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") 751 lexical = lexical.prev; 752 var type = lexical.type, closing = firstChar == type; 753 754 if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); 755 else if (type == "form" && firstChar == "{") return lexical.indented; 756 else if (type == "form") return lexical.indented + indentUnit; 757 else if (type == "stat") 758 return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); 759 else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) 760 return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); 761 else if (lexical.align) return lexical.column + (closing ? 0 : 1); 762 else return lexical.indented + (closing ? 0 : indentUnit); 763 }, 764 765 electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, 766 blockCommentStart: jsonMode ? null : "/*", 767 blockCommentEnd: jsonMode ? null : "*/", 768 lineComment: jsonMode ? null : "//", 769 fold: "brace", 770 closeBrackets: "()[]{}''\"\"``", 771 772 helperType: jsonMode ? "json" : "javascript", 773 jsonldMode: jsonldMode, 774 jsonMode: jsonMode, 775 776 expressionAllowed: expressionAllowed, 777 skipExpression: function(state) { 778 var top = state.cc[state.cc.length - 1] 779 if (top == expression || top == expressionNoComma) state.cc.pop() 780 } 781 }; 782 }); 783 784 CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); 785 786 CodeMirror.defineMIME("text/javascript", "javascript"); 787 CodeMirror.defineMIME("text/ecmascript", "javascript"); 788 CodeMirror.defineMIME("application/javascript", "javascript"); 789 CodeMirror.defineMIME("application/x-javascript", "javascript"); 790 CodeMirror.defineMIME("application/ecmascript", "javascript"); 791 CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); 792 CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); 793 CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); 794 CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); 795 CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); 796 797 }); -
src/wp-includes/js/codemirror/mode/markdown/markdown.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror", "../xml/xml", "../meta"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { 15 16 var htmlMode = CodeMirror.getMode(cmCfg, "text/html"); 17 var htmlModeMissing = htmlMode.name == "null" 18 19 function getMode(name) { 20 if (CodeMirror.findModeByName) { 21 var found = CodeMirror.findModeByName(name); 22 if (found) name = found.mime || found.mimes[0]; 23 } 24 var mode = CodeMirror.getMode(cmCfg, name); 25 return mode.name == "null" ? null : mode; 26 } 27 28 // Should characters that affect highlighting be highlighted separate? 29 // Does not include characters that will be output (such as `1.` and `-` for lists) 30 if (modeCfg.highlightFormatting === undefined) 31 modeCfg.highlightFormatting = false; 32 33 // Maximum number of nested blockquotes. Set to 0 for infinite nesting. 34 // Excess `>` will emit `error` token. 35 if (modeCfg.maxBlockquoteDepth === undefined) 36 modeCfg.maxBlockquoteDepth = 0; 37 38 // Should underscores in words open/close em/strong? 39 if (modeCfg.underscoresBreakWords === undefined) 40 modeCfg.underscoresBreakWords = true; 41 42 // Use `fencedCodeBlocks` to configure fenced code blocks. false to 43 // disable, string to specify a precise regexp that the fence should 44 // match, and true to allow three or more backticks or tildes (as 45 // per CommonMark). 46 47 // Turn on task lists? ("- [ ] " and "- [x] ") 48 if (modeCfg.taskLists === undefined) modeCfg.taskLists = false; 49 50 // Turn on strikethrough syntax 51 if (modeCfg.strikethrough === undefined) 52 modeCfg.strikethrough = false; 53 54 // Allow token types to be overridden by user-provided token types. 55 if (modeCfg.tokenTypeOverrides === undefined) 56 modeCfg.tokenTypeOverrides = {}; 57 58 var tokenTypes = { 59 header: "header", 60 code: "comment", 61 quote: "quote", 62 list1: "variable-2", 63 list2: "variable-3", 64 list3: "keyword", 65 hr: "hr", 66 image: "image", 67 imageAltText: "image-alt-text", 68 imageMarker: "image-marker", 69 formatting: "formatting", 70 linkInline: "link", 71 linkEmail: "link", 72 linkText: "link", 73 linkHref: "string", 74 em: "em", 75 strong: "strong", 76 strikethrough: "strikethrough" 77 }; 78 79 for (var tokenType in tokenTypes) { 80 if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) { 81 tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType]; 82 } 83 } 84 85 var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/ 86 , listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/ 87 , taskListRE = /^\[(x| )\](?=\s)/ // Must follow listRE 88 , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/ 89 , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/ 90 , textRE = /^[^#!\[\]*_\\<>` "'(~]+/ 91 , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) + 92 ")[ \\t]*([\\w+#\-]*)"); 93 94 function switchInline(stream, state, f) { 95 state.f = state.inline = f; 96 return f(stream, state); 97 } 98 99 function switchBlock(stream, state, f) { 100 state.f = state.block = f; 101 return f(stream, state); 102 } 103 104 function lineIsEmpty(line) { 105 return !line || !/\S/.test(line.string) 106 } 107 108 // Blocks 109 110 function blankLine(state) { 111 // Reset linkTitle state 112 state.linkTitle = false; 113 // Reset EM state 114 state.em = false; 115 // Reset STRONG state 116 state.strong = false; 117 // Reset strikethrough state 118 state.strikethrough = false; 119 // Reset state.quote 120 state.quote = 0; 121 // Reset state.indentedCode 122 state.indentedCode = false; 123 if (htmlModeMissing && state.f == htmlBlock) { 124 state.f = inlineNormal; 125 state.block = blockNormal; 126 } 127 // Reset state.trailingSpace 128 state.trailingSpace = 0; 129 state.trailingSpaceNewLine = false; 130 // Mark this line as blank 131 state.prevLine = state.thisLine 132 state.thisLine = null 133 return null; 134 } 135 136 function blockNormal(stream, state) { 137 138 var sol = stream.sol(); 139 140 var prevLineIsList = state.list !== false, 141 prevLineIsIndentedCode = state.indentedCode; 142 143 state.indentedCode = false; 144 145 if (prevLineIsList) { 146 if (state.indentationDiff >= 0) { // Continued list 147 if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block 148 state.indentation -= state.indentationDiff; 149 } 150 state.list = null; 151 } else if (state.indentation > 0) { 152 state.list = null; 153 } else { // No longer a list 154 state.list = false; 155 } 156 } 157 158 var match = null; 159 if (state.indentationDiff >= 4) { 160 stream.skipToEnd(); 161 if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) { 162 state.indentation -= 4; 163 state.indentedCode = true; 164 return tokenTypes.code; 165 } else { 166 return null; 167 } 168 } else if (stream.eatSpace()) { 169 return null; 170 } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) { 171 state.header = match[1].length; 172 if (modeCfg.highlightFormatting) state.formatting = "header"; 173 state.f = state.inline; 174 return getType(state); 175 } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList && 176 !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) { 177 state.header = match[0].charAt(0) == '=' ? 1 : 2; 178 if (modeCfg.highlightFormatting) state.formatting = "header"; 179 state.f = state.inline; 180 return getType(state); 181 } else if (stream.eat('>')) { 182 state.quote = sol ? 1 : state.quote + 1; 183 if (modeCfg.highlightFormatting) state.formatting = "quote"; 184 stream.eatSpace(); 185 return getType(state); 186 } else if (stream.peek() === '[') { 187 return switchInline(stream, state, footnoteLink); 188 } else if (stream.match(hrRE, true)) { 189 state.hr = true; 190 return tokenTypes.hr; 191 } else if (match = stream.match(listRE)) { 192 var listType = match[1] ? "ol" : "ul"; 193 state.indentation = stream.column() + stream.current().length; 194 state.list = true; 195 196 // While this list item's marker's indentation 197 // is less than the deepest list item's content's indentation, 198 // pop the deepest list item indentation off the stack. 199 while (state.listStack && stream.column() < state.listStack[state.listStack.length - 1]) { 200 state.listStack.pop(); 201 } 202 203 // Add this list item's content's indentation to the stack 204 state.listStack.push(state.indentation); 205 206 if (modeCfg.taskLists && stream.match(taskListRE, false)) { 207 state.taskList = true; 208 } 209 state.f = state.inline; 210 if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType]; 211 return getType(state); 212 } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) { 213 state.fencedChars = match[1] 214 // try switching mode 215 state.localMode = getMode(match[2]); 216 if (state.localMode) state.localState = CodeMirror.startState(state.localMode); 217 state.f = state.block = local; 218 if (modeCfg.highlightFormatting) state.formatting = "code-block"; 219 state.code = -1 220 return getType(state); 221 } 222 223 return switchInline(stream, state, state.inline); 224 } 225 226 function htmlBlock(stream, state) { 227 var style = htmlMode.token(stream, state.htmlState); 228 if (!htmlModeMissing) { 229 var inner = CodeMirror.innerMode(htmlMode, state.htmlState) 230 if ((inner.mode.name == "xml" && inner.state.tagStart === null && 231 (!inner.state.context && inner.state.tokenize.isInText)) || 232 (state.md_inside && stream.current().indexOf(">") > -1)) { 233 state.f = inlineNormal; 234 state.block = blockNormal; 235 state.htmlState = null; 236 } 237 } 238 return style; 239 } 240 241 function local(stream, state) { 242 if (state.fencedChars && stream.match(state.fencedChars, false)) { 243 state.localMode = state.localState = null; 244 state.f = state.block = leavingLocal; 245 return null; 246 } else if (state.localMode) { 247 return state.localMode.token(stream, state.localState); 248 } else { 249 stream.skipToEnd(); 250 return tokenTypes.code; 251 } 252 } 253 254 function leavingLocal(stream, state) { 255 stream.match(state.fencedChars); 256 state.block = blockNormal; 257 state.f = inlineNormal; 258 state.fencedChars = null; 259 if (modeCfg.highlightFormatting) state.formatting = "code-block"; 260 state.code = 1 261 var returnType = getType(state); 262 state.code = 0 263 return returnType; 264 } 265 266 // Inline 267 function getType(state) { 268 var styles = []; 269 270 if (state.formatting) { 271 styles.push(tokenTypes.formatting); 272 273 if (typeof state.formatting === "string") state.formatting = [state.formatting]; 274 275 for (var i = 0; i < state.formatting.length; i++) { 276 styles.push(tokenTypes.formatting + "-" + state.formatting[i]); 277 278 if (state.formatting[i] === "header") { 279 styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header); 280 } 281 282 // Add `formatting-quote` and `formatting-quote-#` for blockquotes 283 // Add `error` instead if the maximum blockquote nesting depth is passed 284 if (state.formatting[i] === "quote") { 285 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) { 286 styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote); 287 } else { 288 styles.push("error"); 289 } 290 } 291 } 292 } 293 294 if (state.taskOpen) { 295 styles.push("meta"); 296 return styles.length ? styles.join(' ') : null; 297 } 298 if (state.taskClosed) { 299 styles.push("property"); 300 return styles.length ? styles.join(' ') : null; 301 } 302 303 if (state.linkHref) { 304 styles.push(tokenTypes.linkHref, "url"); 305 } else { // Only apply inline styles to non-url text 306 if (state.strong) { styles.push(tokenTypes.strong); } 307 if (state.em) { styles.push(tokenTypes.em); } 308 if (state.strikethrough) { styles.push(tokenTypes.strikethrough); } 309 if (state.linkText) { styles.push(tokenTypes.linkText); } 310 if (state.code) { styles.push(tokenTypes.code); } 311 if (state.image) { styles.push(tokenTypes.image); } 312 if (state.imageAltText) { styles.push(tokenTypes.imageAltText, "link"); } 313 if (state.imageMarker) { styles.push(tokenTypes.imageMarker); } 314 } 315 316 if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); } 317 318 if (state.quote) { 319 styles.push(tokenTypes.quote); 320 321 // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth 322 if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) { 323 styles.push(tokenTypes.quote + "-" + state.quote); 324 } else { 325 styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth); 326 } 327 } 328 329 if (state.list !== false) { 330 var listMod = (state.listStack.length - 1) % 3; 331 if (!listMod) { 332 styles.push(tokenTypes.list1); 333 } else if (listMod === 1) { 334 styles.push(tokenTypes.list2); 335 } else { 336 styles.push(tokenTypes.list3); 337 } 338 } 339 340 if (state.trailingSpaceNewLine) { 341 styles.push("trailing-space-new-line"); 342 } else if (state.trailingSpace) { 343 styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b")); 344 } 345 346 return styles.length ? styles.join(' ') : null; 347 } 348 349 function handleText(stream, state) { 350 if (stream.match(textRE, true)) { 351 return getType(state); 352 } 353 return undefined; 354 } 355 356 function inlineNormal(stream, state) { 357 var style = state.text(stream, state); 358 if (typeof style !== 'undefined') 359 return style; 360 361 if (state.list) { // List marker (*, +, -, 1., etc) 362 state.list = null; 363 return getType(state); 364 } 365 366 if (state.taskList) { 367 var taskOpen = stream.match(taskListRE, true)[1] !== "x"; 368 if (taskOpen) state.taskOpen = true; 369 else state.taskClosed = true; 370 if (modeCfg.highlightFormatting) state.formatting = "task"; 371 state.taskList = false; 372 return getType(state); 373 } 374 375 state.taskOpen = false; 376 state.taskClosed = false; 377 378 if (state.header && stream.match(/^#+$/, true)) { 379 if (modeCfg.highlightFormatting) state.formatting = "header"; 380 return getType(state); 381 } 382 383 // Get sol() value now, before character is consumed 384 var sol = stream.sol(); 385 386 var ch = stream.next(); 387 388 // Matches link titles present on next line 389 if (state.linkTitle) { 390 state.linkTitle = false; 391 var matchCh = ch; 392 if (ch === '(') { 393 matchCh = ')'; 394 } 395 matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); 396 var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh; 397 if (stream.match(new RegExp(regex), true)) { 398 return tokenTypes.linkHref; 399 } 400 } 401 402 // If this block is changed, it may need to be updated in GFM mode 403 if (ch === '`') { 404 var previousFormatting = state.formatting; 405 if (modeCfg.highlightFormatting) state.formatting = "code"; 406 stream.eatWhile('`'); 407 var count = stream.current().length 408 if (state.code == 0) { 409 state.code = count 410 return getType(state) 411 } else if (count == state.code) { // Must be exact 412 var t = getType(state) 413 state.code = 0 414 return t 415 } else { 416 state.formatting = previousFormatting 417 return getType(state) 418 } 419 } else if (state.code) { 420 return getType(state); 421 } 422 423 if (ch === '\\') { 424 stream.next(); 425 if (modeCfg.highlightFormatting) { 426 var type = getType(state); 427 var formattingEscape = tokenTypes.formatting + "-escape"; 428 return type ? type + " " + formattingEscape : formattingEscape; 429 } 430 } 431 432 if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) { 433 state.imageMarker = true; 434 state.image = true; 435 if (modeCfg.highlightFormatting) state.formatting = "image"; 436 return getType(state); 437 } 438 439 if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) { 440 state.imageMarker = false; 441 state.imageAltText = true 442 if (modeCfg.highlightFormatting) state.formatting = "image"; 443 return getType(state); 444 } 445 446 if (ch === ']' && state.imageAltText) { 447 if (modeCfg.highlightFormatting) state.formatting = "image"; 448 var type = getType(state); 449 state.imageAltText = false; 450 state.image = false; 451 state.inline = state.f = linkHref; 452 return type; 453 } 454 455 if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false) && !state.image) { 456 state.linkText = true; 457 if (modeCfg.highlightFormatting) state.formatting = "link"; 458 return getType(state); 459 } 460 461 if (ch === ']' && state.linkText && stream.match(/\(.*?\)| ?\[.*?\]/, false)) { 462 if (modeCfg.highlightFormatting) state.formatting = "link"; 463 var type = getType(state); 464 state.linkText = false; 465 state.inline = state.f = linkHref; 466 return type; 467 } 468 469 if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) { 470 state.f = state.inline = linkInline; 471 if (modeCfg.highlightFormatting) state.formatting = "link"; 472 var type = getType(state); 473 if (type){ 474 type += " "; 475 } else { 476 type = ""; 477 } 478 return type + tokenTypes.linkInline; 479 } 480 481 if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) { 482 state.f = state.inline = linkInline; 483 if (modeCfg.highlightFormatting) state.formatting = "link"; 484 var type = getType(state); 485 if (type){ 486 type += " "; 487 } else { 488 type = ""; 489 } 490 return type + tokenTypes.linkEmail; 491 } 492 493 if (ch === '<' && stream.match(/^(!--|[a-z]+(?:\s+[a-z_:.\-]+(?:\s*=\s*[^ >]+)?)*\s*>)/i, false)) { 494 var end = stream.string.indexOf(">", stream.pos); 495 if (end != -1) { 496 var atts = stream.string.substring(stream.start, end); 497 if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true; 498 } 499 stream.backUp(1); 500 state.htmlState = CodeMirror.startState(htmlMode); 501 return switchBlock(stream, state, htmlBlock); 502 } 503 504 if (ch === '<' && stream.match(/^\/\w*?>/)) { 505 state.md_inside = false; 506 return "tag"; 507 } 508 509 var ignoreUnderscore = false; 510 if (!modeCfg.underscoresBreakWords) { 511 if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) { 512 var prevPos = stream.pos - 2; 513 if (prevPos >= 0) { 514 var prevCh = stream.string.charAt(prevPos); 515 if (prevCh !== '_' && prevCh.match(/(\w)/, false)) { 516 ignoreUnderscore = true; 517 } 518 } 519 } 520 } 521 if (ch === '*' || (ch === '_' && !ignoreUnderscore)) { 522 if (sol && stream.peek() === ' ') { 523 // Do nothing, surrounded by newline and space 524 } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG 525 if (modeCfg.highlightFormatting) state.formatting = "strong"; 526 var t = getType(state); 527 state.strong = false; 528 return t; 529 } else if (!state.strong && stream.eat(ch)) { // Add STRONG 530 state.strong = ch; 531 if (modeCfg.highlightFormatting) state.formatting = "strong"; 532 return getType(state); 533 } else if (state.em === ch) { // Remove EM 534 if (modeCfg.highlightFormatting) state.formatting = "em"; 535 var t = getType(state); 536 state.em = false; 537 return t; 538 } else if (!state.em) { // Add EM 539 state.em = ch; 540 if (modeCfg.highlightFormatting) state.formatting = "em"; 541 return getType(state); 542 } 543 } else if (ch === ' ') { 544 if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces 545 if (stream.peek() === ' ') { // Surrounded by spaces, ignore 546 return getType(state); 547 } else { // Not surrounded by spaces, back up pointer 548 stream.backUp(1); 549 } 550 } 551 } 552 553 if (modeCfg.strikethrough) { 554 if (ch === '~' && stream.eatWhile(ch)) { 555 if (state.strikethrough) {// Remove strikethrough 556 if (modeCfg.highlightFormatting) state.formatting = "strikethrough"; 557 var t = getType(state); 558 state.strikethrough = false; 559 return t; 560 } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough 561 state.strikethrough = true; 562 if (modeCfg.highlightFormatting) state.formatting = "strikethrough"; 563 return getType(state); 564 } 565 } else if (ch === ' ') { 566 if (stream.match(/^~~/, true)) { // Probably surrounded by space 567 if (stream.peek() === ' ') { // Surrounded by spaces, ignore 568 return getType(state); 569 } else { // Not surrounded by spaces, back up pointer 570 stream.backUp(2); 571 } 572 } 573 } 574 } 575 576 if (ch === ' ') { 577 if (stream.match(/ +$/, false)) { 578 state.trailingSpace++; 579 } else if (state.trailingSpace) { 580 state.trailingSpaceNewLine = true; 581 } 582 } 583 584 return getType(state); 585 } 586 587 function linkInline(stream, state) { 588 var ch = stream.next(); 589 590 if (ch === ">") { 591 state.f = state.inline = inlineNormal; 592 if (modeCfg.highlightFormatting) state.formatting = "link"; 593 var type = getType(state); 594 if (type){ 595 type += " "; 596 } else { 597 type = ""; 598 } 599 return type + tokenTypes.linkInline; 600 } 601 602 stream.match(/^[^>]+/, true); 603 604 return tokenTypes.linkInline; 605 } 606 607 function linkHref(stream, state) { 608 // Check if space, and return NULL if so (to avoid marking the space) 609 if(stream.eatSpace()){ 610 return null; 611 } 612 var ch = stream.next(); 613 if (ch === '(' || ch === '[') { 614 state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]", 0); 615 if (modeCfg.highlightFormatting) state.formatting = "link-string"; 616 state.linkHref = true; 617 return getType(state); 618 } 619 return 'error'; 620 } 621 622 var linkRE = { 623 ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/, 624 "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\\]]|\\.)*\])*?(?=\])/ 625 } 626 627 function getLinkHrefInside(endChar) { 628 return function(stream, state) { 629 var ch = stream.next(); 630 631 if (ch === endChar) { 632 state.f = state.inline = inlineNormal; 633 if (modeCfg.highlightFormatting) state.formatting = "link-string"; 634 var returnState = getType(state); 635 state.linkHref = false; 636 return returnState; 637 } 638 639 stream.match(linkRE[endChar]) 640 state.linkHref = true; 641 return getType(state); 642 }; 643 } 644 645 function footnoteLink(stream, state) { 646 if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) { 647 state.f = footnoteLinkInside; 648 stream.next(); // Consume [ 649 if (modeCfg.highlightFormatting) state.formatting = "link"; 650 state.linkText = true; 651 return getType(state); 652 } 653 return switchInline(stream, state, inlineNormal); 654 } 655 656 function footnoteLinkInside(stream, state) { 657 if (stream.match(/^\]:/, true)) { 658 state.f = state.inline = footnoteUrl; 659 if (modeCfg.highlightFormatting) state.formatting = "link"; 660 var returnType = getType(state); 661 state.linkText = false; 662 return returnType; 663 } 664 665 stream.match(/^([^\]\\]|\\.)+/, true); 666 667 return tokenTypes.linkText; 668 } 669 670 function footnoteUrl(stream, state) { 671 // Check if space, and return NULL if so (to avoid marking the space) 672 if(stream.eatSpace()){ 673 return null; 674 } 675 // Match URL 676 stream.match(/^[^\s]+/, true); 677 // Check for link title 678 if (stream.peek() === undefined) { // End of line, set flag to check next line 679 state.linkTitle = true; 680 } else { // More content on line, check if link title 681 stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true); 682 } 683 state.f = state.inline = inlineNormal; 684 return tokenTypes.linkHref + " url"; 685 } 686 687 var mode = { 688 startState: function() { 689 return { 690 f: blockNormal, 691 692 prevLine: null, 693 thisLine: null, 694 695 block: blockNormal, 696 htmlState: null, 697 indentation: 0, 698 699 inline: inlineNormal, 700 text: handleText, 701 702 formatting: false, 703 linkText: false, 704 linkHref: false, 705 linkTitle: false, 706 code: 0, 707 em: false, 708 strong: false, 709 header: 0, 710 hr: false, 711 taskList: false, 712 list: false, 713 listStack: [], 714 quote: 0, 715 trailingSpace: 0, 716 trailingSpaceNewLine: false, 717 strikethrough: false, 718 fencedChars: null 719 }; 720 }, 721 722 copyState: function(s) { 723 return { 724 f: s.f, 725 726 prevLine: s.prevLine, 727 thisLine: s.thisLine, 728 729 block: s.block, 730 htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState), 731 indentation: s.indentation, 732 733 localMode: s.localMode, 734 localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null, 735 736 inline: s.inline, 737 text: s.text, 738 formatting: false, 739 linkTitle: s.linkTitle, 740 code: s.code, 741 em: s.em, 742 strong: s.strong, 743 strikethrough: s.strikethrough, 744 header: s.header, 745 hr: s.hr, 746 taskList: s.taskList, 747 list: s.list, 748 listStack: s.listStack.slice(0), 749 quote: s.quote, 750 indentedCode: s.indentedCode, 751 trailingSpace: s.trailingSpace, 752 trailingSpaceNewLine: s.trailingSpaceNewLine, 753 md_inside: s.md_inside, 754 fencedChars: s.fencedChars 755 }; 756 }, 757 758 token: function(stream, state) { 759 760 // Reset state.formatting 761 state.formatting = false; 762 763 if (stream != state.thisLine) { 764 var forceBlankLine = state.header || state.hr; 765 766 // Reset state.header and state.hr 767 state.header = 0; 768 state.hr = false; 769 770 if (stream.match(/^\s*$/, true) || forceBlankLine) { 771 blankLine(state); 772 if (!forceBlankLine) return null 773 state.prevLine = null 774 } 775 776 state.prevLine = state.thisLine 777 state.thisLine = stream 778 779 // Reset state.taskList 780 state.taskList = false; 781 782 // Reset state.trailingSpace 783 state.trailingSpace = 0; 784 state.trailingSpaceNewLine = false; 785 786 state.f = state.block; 787 var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length; 788 state.indentationDiff = Math.min(indentation - state.indentation, 4); 789 state.indentation = state.indentation + state.indentationDiff; 790 if (indentation > 0) return null; 791 } 792 return state.f(stream, state); 793 }, 794 795 innerMode: function(state) { 796 if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode}; 797 if (state.localState) return {state: state.localState, mode: state.localMode}; 798 return {state: state, mode: mode}; 799 }, 800 801 blankLine: blankLine, 802 803 getType: getType, 804 805 closeBrackets: "()[]{}''\"\"``", 806 fold: "markdown" 807 }; 808 return mode; 809 }, "xml"); 810 811 CodeMirror.defineMIME("text/x-markdown", "markdown"); 812 813 }); -
src/wp-includes/js/codemirror/mode/php/php.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 function keywords(str) { 15 var obj = {}, words = str.split(" "); 16 for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 17 return obj; 18 } 19 20 // Helper for phpString 21 function matchSequence(list, end, escapes) { 22 if (list.length == 0) return phpString(end); 23 return function (stream, state) { 24 var patterns = list[0]; 25 for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) { 26 state.tokenize = matchSequence(list.slice(1), end); 27 return patterns[i][1]; 28 } 29 state.tokenize = phpString(end, escapes); 30 return "string"; 31 }; 32 } 33 function phpString(closing, escapes) { 34 return function(stream, state) { return phpString_(stream, state, closing, escapes); }; 35 } 36 function phpString_(stream, state, closing, escapes) { 37 // "Complex" syntax 38 if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) { 39 state.tokenize = null; 40 return "string"; 41 } 42 43 // Simple syntax 44 if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) { 45 // After the variable name there may appear array or object operator. 46 if (stream.match("[", false)) { 47 // Match array operator 48 state.tokenize = matchSequence([ 49 [["[", null]], 50 [[/\d[\w\.]*/, "number"], 51 [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"], 52 [/[\w\$]+/, "variable"]], 53 [["]", null]] 54 ], closing, escapes); 55 } 56 if (stream.match(/\-\>\w/, false)) { 57 // Match object operator 58 state.tokenize = matchSequence([ 59 [["->", null]], 60 [[/[\w]+/, "variable"]] 61 ], closing, escapes); 62 } 63 return "variable-2"; 64 } 65 66 var escaped = false; 67 // Normal string 68 while (!stream.eol() && 69 (escaped || escapes === false || 70 (!stream.match("{$", false) && 71 !stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) { 72 if (!escaped && stream.match(closing)) { 73 state.tokenize = null; 74 state.tokStack.pop(); state.tokStack.pop(); 75 break; 76 } 77 escaped = stream.next() == "\\" && !escaped; 78 } 79 return "string"; 80 } 81 82 var phpKeywords = "abstract and array as break case catch class clone const continue declare default " + 83 "do else elseif enddeclare endfor endforeach endif endswitch endwhile extends final " + 84 "for foreach function global goto if implements interface instanceof namespace " + 85 "new or private protected public static switch throw trait try use var while xor " + 86 "die echo empty exit eval include include_once isset list require require_once return " + 87 "print unset __halt_compiler self static parent yield insteadof finally"; 88 var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__"; 89 var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents file_put_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists array_intersect_key array_combine array_column pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count"; 90 CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" ")); 91 CodeMirror.registerHelper("wordChars", "php", /[\w$]/); 92 93 var phpConfig = { 94 name: "clike", 95 helperType: "php", 96 keywords: keywords(phpKeywords), 97 blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"), 98 defKeywords: keywords("class function interface namespace trait"), 99 atoms: keywords(phpAtoms), 100 builtin: keywords(phpBuiltin), 101 multiLineStrings: true, 102 hooks: { 103 "$": function(stream) { 104 stream.eatWhile(/[\w\$_]/); 105 return "variable-2"; 106 }, 107 "<": function(stream, state) { 108 var before; 109 if (before = stream.match(/<<\s*/)) { 110 var quoted = stream.eat(/['"]/); 111 stream.eatWhile(/[\w\.]/); 112 var delim = stream.current().slice(before[0].length + (quoted ? 2 : 1)); 113 if (quoted) stream.eat(quoted); 114 if (delim) { 115 (state.tokStack || (state.tokStack = [])).push(delim, 0); 116 state.tokenize = phpString(delim, quoted != "'"); 117 return "string"; 118 } 119 } 120 return false; 121 }, 122 "#": function(stream) { 123 while (!stream.eol() && !stream.match("?>", false)) stream.next(); 124 return "comment"; 125 }, 126 "/": function(stream) { 127 if (stream.eat("/")) { 128 while (!stream.eol() && !stream.match("?>", false)) stream.next(); 129 return "comment"; 130 } 131 return false; 132 }, 133 '"': function(_stream, state) { 134 (state.tokStack || (state.tokStack = [])).push('"', 0); 135 state.tokenize = phpString('"'); 136 return "string"; 137 }, 138 "{": function(_stream, state) { 139 if (state.tokStack && state.tokStack.length) 140 state.tokStack[state.tokStack.length - 1]++; 141 return false; 142 }, 143 "}": function(_stream, state) { 144 if (state.tokStack && state.tokStack.length > 0 && 145 !--state.tokStack[state.tokStack.length - 1]) { 146 state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]); 147 } 148 return false; 149 } 150 } 151 }; 152 153 CodeMirror.defineMode("php", function(config, parserConfig) { 154 var htmlMode = CodeMirror.getMode(config, "text/html"); 155 var phpMode = CodeMirror.getMode(config, phpConfig); 156 157 function dispatch(stream, state) { 158 var isPHP = state.curMode == phpMode; 159 if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null; 160 if (!isPHP) { 161 if (stream.match(/^<\?\w*/)) { 162 state.curMode = phpMode; 163 if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, "")) 164 state.curState = state.php; 165 return "meta"; 166 } 167 if (state.pending == '"' || state.pending == "'") { 168 while (!stream.eol() && stream.next() != state.pending) {} 169 var style = "string"; 170 } else if (state.pending && stream.pos < state.pending.end) { 171 stream.pos = state.pending.end; 172 var style = state.pending.style; 173 } else { 174 var style = htmlMode.token(stream, state.curState); 175 } 176 if (state.pending) state.pending = null; 177 var cur = stream.current(), openPHP = cur.search(/<\?/), m; 178 if (openPHP != -1) { 179 if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0]; 180 else state.pending = {end: stream.pos, style: style}; 181 stream.backUp(cur.length - openPHP); 182 } 183 return style; 184 } else if (isPHP && state.php.tokenize == null && stream.match("?>")) { 185 state.curMode = htmlMode; 186 state.curState = state.html; 187 if (!state.php.context.prev) state.php = null; 188 return "meta"; 189 } else { 190 return phpMode.token(stream, state.curState); 191 } 192 } 193 194 return { 195 startState: function() { 196 var html = CodeMirror.startState(htmlMode) 197 var php = parserConfig.startOpen ? CodeMirror.startState(phpMode) : null 198 return {html: html, 199 php: php, 200 curMode: parserConfig.startOpen ? phpMode : htmlMode, 201 curState: parserConfig.startOpen ? php : html, 202 pending: null}; 203 }, 204 205 copyState: function(state) { 206 var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html), 207 php = state.php, phpNew = php && CodeMirror.copyState(phpMode, php), cur; 208 if (state.curMode == htmlMode) cur = htmlNew; 209 else cur = phpNew; 210 return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur, 211 pending: state.pending}; 212 }, 213 214 token: dispatch, 215 216 indent: function(state, textAfter) { 217 if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) || 218 (state.curMode == phpMode && /^\?>/.test(textAfter))) 219 return htmlMode.indent(state.html, textAfter); 220 return state.curMode.indent(state.curState, textAfter); 221 }, 222 223 blockCommentStart: "/*", 224 blockCommentEnd: "*/", 225 lineComment: "//", 226 227 innerMode: function(state) { return {state: state.curState, mode: state.curMode}; } 228 }; 229 }, "htmlmixed", "clike"); 230 231 CodeMirror.defineMIME("application/x-httpd-php", "php"); 232 CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true}); 233 CodeMirror.defineMIME("text/x-php", phpConfig); 234 }); -
src/wp-includes/js/codemirror/mode/shell/shell.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineMode('shell', function() { 15 16 var words = {}; 17 function define(style, string) { 18 var split = string.split(' '); 19 for(var i = 0; i < split.length; i++) { 20 words[split[i]] = style; 21 } 22 }; 23 24 // Atoms 25 define('atom', 'true false'); 26 27 // Keywords 28 define('keyword', 'if then do else elif while until for in esac fi fin ' + 29 'fil done exit set unset export function'); 30 31 // Commands 32 define('builtin', 'ab awk bash beep cat cc cd chown chmod chroot clear cp ' + 33 'curl cut diff echo find gawk gcc get git grep kill killall ln ls make ' + 34 'mkdir openssl mv nc node npm ping ps restart rm rmdir sed service sh ' + 35 'shopt shred source sort sleep ssh start stop su sudo tee telnet top ' + 36 'touch vi vim wall wc wget who write yes zsh'); 37 38 function tokenBase(stream, state) { 39 if (stream.eatSpace()) return null; 40 41 var sol = stream.sol(); 42 var ch = stream.next(); 43 44 if (ch === '\\') { 45 stream.next(); 46 return null; 47 } 48 if (ch === '\'' || ch === '"' || ch === '`') { 49 state.tokens.unshift(tokenString(ch)); 50 return tokenize(stream, state); 51 } 52 if (ch === '#') { 53 if (sol && stream.eat('!')) { 54 stream.skipToEnd(); 55 return 'meta'; // 'comment'? 56 } 57 stream.skipToEnd(); 58 return 'comment'; 59 } 60 if (ch === '$') { 61 state.tokens.unshift(tokenDollar); 62 return tokenize(stream, state); 63 } 64 if (ch === '+' || ch === '=') { 65 return 'operator'; 66 } 67 if (ch === '-') { 68 stream.eat('-'); 69 stream.eatWhile(/\w/); 70 return 'attribute'; 71 } 72 if (/\d/.test(ch)) { 73 stream.eatWhile(/\d/); 74 if(stream.eol() || !/\w/.test(stream.peek())) { 75 return 'number'; 76 } 77 } 78 stream.eatWhile(/[\w-]/); 79 var cur = stream.current(); 80 if (stream.peek() === '=' && /\w+/.test(cur)) return 'def'; 81 return words.hasOwnProperty(cur) ? words[cur] : null; 82 } 83 84 function tokenString(quote) { 85 return function(stream, state) { 86 var next, end = false, escaped = false; 87 while ((next = stream.next()) != null) { 88 if (next === quote && !escaped) { 89 end = true; 90 break; 91 } 92 if (next === '$' && !escaped && quote !== '\'') { 93 escaped = true; 94 stream.backUp(1); 95 state.tokens.unshift(tokenDollar); 96 break; 97 } 98 escaped = !escaped && next === '\\'; 99 } 100 if (end || !escaped) { 101 state.tokens.shift(); 102 } 103 return (quote === '`' || quote === ')' ? 'quote' : 'string'); 104 }; 105 }; 106 107 var tokenDollar = function(stream, state) { 108 if (state.tokens.length > 1) stream.eat('$'); 109 var ch = stream.next(), hungry = /\w/; 110 if (ch === '{') hungry = /[^}]/; 111 if (ch === '(') { 112 state.tokens[0] = tokenString(')'); 113 return tokenize(stream, state); 114 } 115 if (!/\d/.test(ch)) { 116 stream.eatWhile(hungry); 117 stream.eat('}'); 118 } 119 state.tokens.shift(); 120 return 'def'; 121 }; 122 123 function tokenize(stream, state) { 124 return (state.tokens[0] || tokenBase) (stream, state); 125 }; 126 127 return { 128 startState: function() {return {tokens:[]};}, 129 token: function(stream, state) { 130 return tokenize(stream, state); 131 }, 132 closeBrackets: "()[]{}''\"\"``", 133 lineComment: '#', 134 fold: "brace" 135 }; 136 }); 137 138 CodeMirror.defineMIME('text/x-sh', 'shell'); 139 140 }); -
src/wp-includes/js/codemirror/mode/sql/sql.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineMode("sql", function(config, parserConfig) { 15 "use strict"; 16 17 var client = parserConfig.client || {}, 18 atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, 19 builtin = parserConfig.builtin || {}, 20 keywords = parserConfig.keywords || {}, 21 operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/, 22 support = parserConfig.support || {}, 23 hooks = parserConfig.hooks || {}, 24 dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}; 25 26 function tokenBase(stream, state) { 27 var ch = stream.next(); 28 29 // call hooks from the mime type 30 if (hooks[ch]) { 31 var result = hooks[ch](stream, state); 32 if (result !== false) return result; 33 } 34 35 if (support.hexNumber && 36 ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) 37 || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) { 38 // hex 39 // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html 40 return "number"; 41 } else if (support.binaryNumber && 42 (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) 43 || (ch == "0" && stream.match(/^b[01]+/)))) { 44 // bitstring 45 // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html 46 return "number"; 47 } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { 48 // numbers 49 // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html 50 stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/); 51 support.decimallessFloat && stream.eat('.'); 52 return "number"; 53 } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) { 54 // placeholders 55 return "variable-3"; 56 } else if (ch == "'" || (ch == '"' && support.doubleQuote)) { 57 // strings 58 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html 59 state.tokenize = tokenLiteral(ch); 60 return state.tokenize(stream, state); 61 } else if ((((support.nCharCast && (ch == "n" || ch == "N")) 62 || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) 63 && (stream.peek() == "'" || stream.peek() == '"'))) { 64 // charset casting: _utf8'str', N'str', n'str' 65 // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html 66 return "keyword"; 67 } else if (/^[\(\),\;\[\]]/.test(ch)) { 68 // no highlighting 69 return null; 70 } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) { 71 // 1-line comment 72 stream.skipToEnd(); 73 return "comment"; 74 } else if ((support.commentHash && ch == "#") 75 || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) { 76 // 1-line comments 77 // ref: https://kb.askmonty.org/en/comment-syntax/ 78 stream.skipToEnd(); 79 return "comment"; 80 } else if (ch == "/" && stream.eat("*")) { 81 // multi-line comments 82 // ref: https://kb.askmonty.org/en/comment-syntax/ 83 state.tokenize = tokenComment; 84 return state.tokenize(stream, state); 85 } else if (ch == ".") { 86 // .1 for 0.1 87 if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) { 88 return "number"; 89 } 90 // .table_name (ODBC) 91 // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html 92 if (support.ODBCdotTable && stream.match(/^[a-zA-Z_]+/)) { 93 return "variable-2"; 94 } 95 } else if (operatorChars.test(ch)) { 96 // operators 97 stream.eatWhile(operatorChars); 98 return null; 99 } else if (ch == '{' && 100 (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) { 101 // dates (weird ODBC syntax) 102 // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html 103 return "number"; 104 } else { 105 stream.eatWhile(/^[_\w\d]/); 106 var word = stream.current().toLowerCase(); 107 // dates (standard SQL syntax) 108 // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html 109 if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/))) 110 return "number"; 111 if (atoms.hasOwnProperty(word)) return "atom"; 112 if (builtin.hasOwnProperty(word)) return "builtin"; 113 if (keywords.hasOwnProperty(word)) return "keyword"; 114 if (client.hasOwnProperty(word)) return "string-2"; 115 return null; 116 } 117 } 118 119 // 'string', with char specified in quote escaped by '\' 120 function tokenLiteral(quote) { 121 return function(stream, state) { 122 var escaped = false, ch; 123 while ((ch = stream.next()) != null) { 124 if (ch == quote && !escaped) { 125 state.tokenize = tokenBase; 126 break; 127 } 128 escaped = !escaped && ch == "\\"; 129 } 130 return "string"; 131 }; 132 } 133 function tokenComment(stream, state) { 134 while (true) { 135 if (stream.skipTo("*")) { 136 stream.next(); 137 if (stream.eat("/")) { 138 state.tokenize = tokenBase; 139 break; 140 } 141 } else { 142 stream.skipToEnd(); 143 break; 144 } 145 } 146 return "comment"; 147 } 148 149 function pushContext(stream, state, type) { 150 state.context = { 151 prev: state.context, 152 indent: stream.indentation(), 153 col: stream.column(), 154 type: type 155 }; 156 } 157 158 function popContext(state) { 159 state.indent = state.context.indent; 160 state.context = state.context.prev; 161 } 162 163 return { 164 startState: function() { 165 return {tokenize: tokenBase, context: null}; 166 }, 167 168 token: function(stream, state) { 169 if (stream.sol()) { 170 if (state.context && state.context.align == null) 171 state.context.align = false; 172 } 173 if (stream.eatSpace()) return null; 174 175 var style = state.tokenize(stream, state); 176 if (style == "comment") return style; 177 178 if (state.context && state.context.align == null) 179 state.context.align = true; 180 181 var tok = stream.current(); 182 if (tok == "(") 183 pushContext(stream, state, ")"); 184 else if (tok == "[") 185 pushContext(stream, state, "]"); 186 else if (state.context && state.context.type == tok) 187 popContext(state); 188 return style; 189 }, 190 191 indent: function(state, textAfter) { 192 var cx = state.context; 193 if (!cx) return CodeMirror.Pass; 194 var closing = textAfter.charAt(0) == cx.type; 195 if (cx.align) return cx.col + (closing ? 0 : 1); 196 else return cx.indent + (closing ? 0 : config.indentUnit); 197 }, 198 199 blockCommentStart: "/*", 200 blockCommentEnd: "*/", 201 lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null 202 }; 203 }); 204 205 (function() { 206 "use strict"; 207 208 // `identifier` 209 function hookIdentifier(stream) { 210 // MySQL/MariaDB identifiers 211 // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html 212 var ch; 213 while ((ch = stream.next()) != null) { 214 if (ch == "`" && !stream.eat("`")) return "variable-2"; 215 } 216 stream.backUp(stream.current().length - 1); 217 return stream.eatWhile(/\w/) ? "variable-2" : null; 218 } 219 220 // variable token 221 function hookVar(stream) { 222 // variables 223 // @@prefix.varName @varName 224 // varName can be quoted with ` or ' or " 225 // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html 226 if (stream.eat("@")) { 227 stream.match(/^session\./); 228 stream.match(/^local\./); 229 stream.match(/^global\./); 230 } 231 232 if (stream.eat("'")) { 233 stream.match(/^.*'/); 234 return "variable-2"; 235 } else if (stream.eat('"')) { 236 stream.match(/^.*"/); 237 return "variable-2"; 238 } else if (stream.eat("`")) { 239 stream.match(/^.*`/); 240 return "variable-2"; 241 } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) { 242 return "variable-2"; 243 } 244 return null; 245 }; 246 247 // short client keyword token 248 function hookClient(stream) { 249 // \N means NULL 250 // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html 251 if (stream.eat("N")) { 252 return "atom"; 253 } 254 // \g, etc 255 // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html 256 return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null; 257 } 258 259 // these keywords are used by all SQL dialects (however, a mode can still overwrite it) 260 var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit "; 261 262 // turn a space-separated list into an array 263 function set(str) { 264 var obj = {}, words = str.split(" "); 265 for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 266 return obj; 267 } 268 269 // A generic SQL Mode. It's not a standard, it just try to support what is generally supported 270 CodeMirror.defineMIME("text/x-sql", { 271 name: "sql", 272 keywords: set(sqlKeywords + "begin"), 273 builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"), 274 atoms: set("false true null unknown"), 275 operatorChars: /^[*+\-%<>!=]/, 276 dateSQL: set("date time timestamp"), 277 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") 278 }); 279 280 CodeMirror.defineMIME("text/x-mssql", { 281 name: "sql", 282 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), 283 keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec"), 284 builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "), 285 atoms: set("false true null unknown"), 286 operatorChars: /^[*+\-%<>!=]/, 287 dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"), 288 hooks: { 289 "@": hookVar 290 } 291 }); 292 293 CodeMirror.defineMIME("text/x-mysql", { 294 name: "sql", 295 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), 296 keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), 297 builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), 298 atoms: set("false true null unknown"), 299 operatorChars: /^[*+\-%<>!=&|^]/, 300 dateSQL: set("date time timestamp"), 301 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), 302 hooks: { 303 "@": hookVar, 304 "`": hookIdentifier, 305 "\\": hookClient 306 } 307 }); 308 309 CodeMirror.defineMIME("text/x-mariadb", { 310 name: "sql", 311 client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), 312 keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated get global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show shutdown signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), 313 builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), 314 atoms: set("false true null unknown"), 315 operatorChars: /^[*+\-%<>!=&|^]/, 316 dateSQL: set("date time timestamp"), 317 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"), 318 hooks: { 319 "@": hookVar, 320 "`": hookIdentifier, 321 "\\": hookClient 322 } 323 }); 324 325 // the query language used by Apache Cassandra is called CQL, but this mime type 326 // is called Cassandra to avoid confusion with Contextual Query Language 327 CodeMirror.defineMIME("text/x-cassandra", { 328 name: "sql", 329 client: { }, 330 keywords: set("add all allow alter and any apply as asc authorize batch begin by clustering columnfamily compact consistency count create custom delete desc distinct drop each_quorum exists filtering from grant if in index insert into key keyspace keyspaces level limit local_one local_quorum modify nan norecursive nosuperuser not of on one order password permission permissions primary quorum rename revoke schema select set storage superuser table three to token truncate ttl two type unlogged update use user users using values where with writetime"), 331 builtin: set("ascii bigint blob boolean counter decimal double float frozen inet int list map static text timestamp timeuuid tuple uuid varchar varint"), 332 atoms: set("false true infinity NaN"), 333 operatorChars: /^[<>=]/, 334 dateSQL: { }, 335 support: set("commentSlashSlash decimallessFloat"), 336 hooks: { } 337 }); 338 339 // this is based on Peter Raganitsch's 'plsql' mode 340 CodeMirror.defineMIME("text/x-plsql", { 341 name: "sql", 342 client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"), 343 keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"), 344 builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"), 345 operatorChars: /^[*+\-%<>!=~]/, 346 dateSQL: set("date time timestamp"), 347 support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber") 348 }); 349 350 // Created to support specific hive keywords 351 CodeMirror.defineMIME("text/x-hive", { 352 name: "sql", 353 keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"), 354 builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"), 355 atoms: set("false true null unknown"), 356 operatorChars: /^[*+\-%<>!=]/, 357 dateSQL: set("date timestamp"), 358 support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") 359 }); 360 361 CodeMirror.defineMIME("text/x-pgsql", { 362 name: "sql", 363 client: set("source"), 364 // http://www.postgresql.org/docs/9.5/static/sql-keywords-appendix.html 365 keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat"), 366 // http://www.postgresql.org/docs/9.5/static/datatype.html 367 builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), 368 atoms: set("false true null unknown"), 369 operatorChars: /^[*+\-%<>!=&|^\/#@?~]/, 370 dateSQL: set("date time timestamp"), 371 support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast") 372 }); 373 374 // Google's SQL-like query language, GQL 375 CodeMirror.defineMIME("text/x-gql", { 376 name: "sql", 377 keywords: set("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"), 378 atoms: set("false true"), 379 builtin: set("blob datetime first key __key__ string integer double boolean null"), 380 operatorChars: /^[*+\-%<>!=]/ 381 }); 382 }()); 383 384 }); 385 386 /* 387 How Properties of Mime Types are used by SQL Mode 388 ================================================= 389 390 keywords: 391 A list of keywords you want to be highlighted. 392 builtin: 393 A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword"). 394 operatorChars: 395 All characters that must be handled as operators. 396 client: 397 Commands parsed and executed by the client (not the server). 398 support: 399 A list of supported syntaxes which are not common, but are supported by more than 1 DBMS. 400 * ODBCdotTable: .tableName 401 * zerolessFloat: .1 402 * doubleQuote 403 * nCharCast: N'string' 404 * charsetCast: _utf8'string' 405 * commentHash: use # char for comments 406 * commentSlashSlash: use // for comments 407 * commentSpaceRequired: require a space after -- for comments 408 atoms: 409 Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others: 410 UNKNOWN, INFINITY, UNDERFLOW, NaN... 411 dateSQL: 412 Used for date/time SQL standard syntax, because not all DBMS's support same temporal types. 413 */ -
src/wp-includes/js/codemirror/mode/xml/xml.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 var htmlConfig = { 15 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, 16 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, 17 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, 18 'track': true, 'wbr': true, 'menuitem': true}, 19 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, 20 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, 21 'th': true, 'tr': true}, 22 contextGrabbers: { 23 'dd': {'dd': true, 'dt': true}, 24 'dt': {'dd': true, 'dt': true}, 25 'li': {'li': true}, 26 'option': {'option': true, 'optgroup': true}, 27 'optgroup': {'optgroup': true}, 28 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, 29 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, 30 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, 31 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, 32 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, 33 'rp': {'rp': true, 'rt': true}, 34 'rt': {'rp': true, 'rt': true}, 35 'tbody': {'tbody': true, 'tfoot': true}, 36 'td': {'td': true, 'th': true}, 37 'tfoot': {'tbody': true}, 38 'th': {'td': true, 'th': true}, 39 'thead': {'tbody': true, 'tfoot': true}, 40 'tr': {'tr': true} 41 }, 42 doNotIndent: {"pre": true}, 43 allowUnquoted: true, 44 allowMissing: true, 45 caseFold: true 46 } 47 48 var xmlConfig = { 49 autoSelfClosers: {}, 50 implicitlyClosed: {}, 51 contextGrabbers: {}, 52 doNotIndent: {}, 53 allowUnquoted: false, 54 allowMissing: false, 55 caseFold: false 56 } 57 58 CodeMirror.defineMode("xml", function(editorConf, config_) { 59 var indentUnit = editorConf.indentUnit 60 var config = {} 61 var defaults = config_.htmlMode ? htmlConfig : xmlConfig 62 for (var prop in defaults) config[prop] = defaults[prop] 63 for (var prop in config_) config[prop] = config_[prop] 64 65 // Return variables for tokenizers 66 var type, setStyle; 67 68 function inText(stream, state) { 69 function chain(parser) { 70 state.tokenize = parser; 71 return parser(stream, state); 72 } 73 74 var ch = stream.next(); 75 if (ch == "<") { 76 if (stream.eat("!")) { 77 if (stream.eat("[")) { 78 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); 79 else return null; 80 } else if (stream.match("--")) { 81 return chain(inBlock("comment", "-->")); 82 } else if (stream.match("DOCTYPE", true, true)) { 83 stream.eatWhile(/[\w\._\-]/); 84 return chain(doctype(1)); 85 } else { 86 return null; 87 } 88 } else if (stream.eat("?")) { 89 stream.eatWhile(/[\w\._\-]/); 90 state.tokenize = inBlock("meta", "?>"); 91 return "meta"; 92 } else { 93 type = stream.eat("/") ? "closeTag" : "openTag"; 94 state.tokenize = inTag; 95 return "tag bracket"; 96 } 97 } else if (ch == "&") { 98 var ok; 99 if (stream.eat("#")) { 100 if (stream.eat("x")) { 101 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); 102 } else { 103 ok = stream.eatWhile(/[\d]/) && stream.eat(";"); 104 } 105 } else { 106 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); 107 } 108 return ok ? "atom" : "error"; 109 } else { 110 stream.eatWhile(/[^&<]/); 111 return null; 112 } 113 } 114 inText.isInText = true; 115 116 function inTag(stream, state) { 117 var ch = stream.next(); 118 if (ch == ">" || (ch == "/" && stream.eat(">"))) { 119 state.tokenize = inText; 120 type = ch == ">" ? "endTag" : "selfcloseTag"; 121 return "tag bracket"; 122 } else if (ch == "=") { 123 type = "equals"; 124 return null; 125 } else if (ch == "<") { 126 state.tokenize = inText; 127 state.state = baseState; 128 state.tagName = state.tagStart = null; 129 var next = state.tokenize(stream, state); 130 return next ? next + " tag error" : "tag error"; 131 } else if (/[\'\"]/.test(ch)) { 132 state.tokenize = inAttribute(ch); 133 state.stringStartCol = stream.column(); 134 return state.tokenize(stream, state); 135 } else { 136 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); 137 return "word"; 138 } 139 } 140 141 function inAttribute(quote) { 142 var closure = function(stream, state) { 143 while (!stream.eol()) { 144 if (stream.next() == quote) { 145 state.tokenize = inTag; 146 break; 147 } 148 } 149 return "string"; 150 }; 151 closure.isInAttribute = true; 152 return closure; 153 } 154 155 function inBlock(style, terminator) { 156 return function(stream, state) { 157 while (!stream.eol()) { 158 if (stream.match(terminator)) { 159 state.tokenize = inText; 160 break; 161 } 162 stream.next(); 163 } 164 return style; 165 }; 166 } 167 function doctype(depth) { 168 return function(stream, state) { 169 var ch; 170 while ((ch = stream.next()) != null) { 171 if (ch == "<") { 172 state.tokenize = doctype(depth + 1); 173 return state.tokenize(stream, state); 174 } else if (ch == ">") { 175 if (depth == 1) { 176 state.tokenize = inText; 177 break; 178 } else { 179 state.tokenize = doctype(depth - 1); 180 return state.tokenize(stream, state); 181 } 182 } 183 } 184 return "meta"; 185 }; 186 } 187 188 function Context(state, tagName, startOfLine) { 189 this.prev = state.context; 190 this.tagName = tagName; 191 this.indent = state.indented; 192 this.startOfLine = startOfLine; 193 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) 194 this.noIndent = true; 195 } 196 function popContext(state) { 197 if (state.context) state.context = state.context.prev; 198 } 199 function maybePopContext(state, nextTagName) { 200 var parentTagName; 201 while (true) { 202 if (!state.context) { 203 return; 204 } 205 parentTagName = state.context.tagName; 206 if (!config.contextGrabbers.hasOwnProperty(parentTagName) || 207 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { 208 return; 209 } 210 popContext(state); 211 } 212 } 213 214 function baseState(type, stream, state) { 215 if (type == "openTag") { 216 state.tagStart = stream.column(); 217 return tagNameState; 218 } else if (type == "closeTag") { 219 return closeTagNameState; 220 } else { 221 return baseState; 222 } 223 } 224 function tagNameState(type, stream, state) { 225 if (type == "word") { 226 state.tagName = stream.current(); 227 setStyle = "tag"; 228 return attrState; 229 } else { 230 setStyle = "error"; 231 return tagNameState; 232 } 233 } 234 function closeTagNameState(type, stream, state) { 235 if (type == "word") { 236 var tagName = stream.current(); 237 if (state.context && state.context.tagName != tagName && 238 config.implicitlyClosed.hasOwnProperty(state.context.tagName)) 239 popContext(state); 240 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { 241 setStyle = "tag"; 242 return closeState; 243 } else { 244 setStyle = "tag error"; 245 return closeStateErr; 246 } 247 } else { 248 setStyle = "error"; 249 return closeStateErr; 250 } 251 } 252 253 function closeState(type, _stream, state) { 254 if (type != "endTag") { 255 setStyle = "error"; 256 return closeState; 257 } 258 popContext(state); 259 return baseState; 260 } 261 function closeStateErr(type, stream, state) { 262 setStyle = "error"; 263 return closeState(type, stream, state); 264 } 265 266 function attrState(type, _stream, state) { 267 if (type == "word") { 268 setStyle = "attribute"; 269 return attrEqState; 270 } else if (type == "endTag" || type == "selfcloseTag") { 271 var tagName = state.tagName, tagStart = state.tagStart; 272 state.tagName = state.tagStart = null; 273 if (type == "selfcloseTag" || 274 config.autoSelfClosers.hasOwnProperty(tagName)) { 275 maybePopContext(state, tagName); 276 } else { 277 maybePopContext(state, tagName); 278 state.context = new Context(state, tagName, tagStart == state.indented); 279 } 280 return baseState; 281 } 282 setStyle = "error"; 283 return attrState; 284 } 285 function attrEqState(type, stream, state) { 286 if (type == "equals") return attrValueState; 287 if (!config.allowMissing) setStyle = "error"; 288 return attrState(type, stream, state); 289 } 290 function attrValueState(type, stream, state) { 291 if (type == "string") return attrContinuedState; 292 if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;} 293 setStyle = "error"; 294 return attrState(type, stream, state); 295 } 296 function attrContinuedState(type, stream, state) { 297 if (type == "string") return attrContinuedState; 298 return attrState(type, stream, state); 299 } 300 301 return { 302 startState: function(baseIndent) { 303 var state = {tokenize: inText, 304 state: baseState, 305 indented: baseIndent || 0, 306 tagName: null, tagStart: null, 307 context: null} 308 if (baseIndent != null) state.baseIndent = baseIndent 309 return state 310 }, 311 312 token: function(stream, state) { 313 if (!state.tagName && stream.sol()) 314 state.indented = stream.indentation(); 315 316 if (stream.eatSpace()) return null; 317 type = null; 318 var style = state.tokenize(stream, state); 319 if ((style || type) && style != "comment") { 320 setStyle = null; 321 state.state = state.state(type || style, stream, state); 322 if (setStyle) 323 style = setStyle == "error" ? style + " error" : setStyle; 324 } 325 return style; 326 }, 327 328 indent: function(state, textAfter, fullLine) { 329 var context = state.context; 330 // Indent multi-line strings (e.g. css). 331 if (state.tokenize.isInAttribute) { 332 if (state.tagStart == state.indented) 333 return state.stringStartCol + 1; 334 else 335 return state.indented + indentUnit; 336 } 337 if (context && context.noIndent) return CodeMirror.Pass; 338 if (state.tokenize != inTag && state.tokenize != inText) 339 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; 340 // Indent the starts of attribute names. 341 if (state.tagName) { 342 if (config.multilineTagIndentPastTag !== false) 343 return state.tagStart + state.tagName.length + 2; 344 else 345 return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); 346 } 347 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0; 348 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter); 349 if (tagAfter && tagAfter[1]) { // Closing tag spotted 350 while (context) { 351 if (context.tagName == tagAfter[2]) { 352 context = context.prev; 353 break; 354 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) { 355 context = context.prev; 356 } else { 357 break; 358 } 359 } 360 } else if (tagAfter) { // Opening tag spotted 361 while (context) { 362 var grabbers = config.contextGrabbers[context.tagName]; 363 if (grabbers && grabbers.hasOwnProperty(tagAfter[2])) 364 context = context.prev; 365 else 366 break; 367 } 368 } 369 while (context && context.prev && !context.startOfLine) 370 context = context.prev; 371 if (context) return context.indent + indentUnit; 372 else return state.baseIndent || 0; 373 }, 374 375 electricInput: /<\/[\s\w:]+>$/, 376 blockCommentStart: "<!--", 377 blockCommentEnd: "-->", 378 379 configuration: config.htmlMode ? "html" : "xml", 380 helperType: config.htmlMode ? "html" : "xml", 381 382 skipAttribute: function(state) { 383 if (state.state == attrValueState) 384 state.state = attrState 385 } 386 }; 387 }); 388 389 CodeMirror.defineMIME("text/xml", "xml"); 390 CodeMirror.defineMIME("application/xml", "xml"); 391 if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) 392 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); 393 394 }); -
src/wp-includes/script-loader.php
511 511 'versionString' => 'wp/v2/', 512 512 ) ); 513 513 514 // CodeMirror 515 $scripts->add( 'codemirror', "/wp-includes/js/codemirror/lib/codemirror.js", array(), '5.23.0' ); 516 517 $scripts->add( 'codemirror-addon-hint-show', "/wp-includes/js/codemirror/addon/hint/show-hint.js", array( 'codemirror' ), '5.23.0' ); 518 $scripts->add( 'codemirror-addon-hint-css', "/wp-includes/js/codemirror/addon/hint/css-hint.js", array( 'codemirror-addon-hint-show', 'codemirror-mode-css' ), '5.23.0' ); 519 $scripts->add( 'codemirror-addon-hint-html', "/wp-includes/js/codemirror/addon/hint/html-hint.js", array( 'codemirror-addon-hint-show', 'codemirror-mode-html' ), '5.23.0' ); 520 $scripts->add( 'codemirror-addon-hint-javascript', "/wp-includes/js/codemirror/addon/hint/javascript-hint.js", array( 'codemirror-addon-hint-show', 'codemirror-mode-javascript' ), '5.23.0' ); 521 $scripts->add( 'codemirror-addon-hint-sql', "/wp-includes/js/codemirror/addon/hint/sql-hint.js", array( 'codemirror-addon-hint-show', 'codemirror-mode-sql' ), '5.23.0' ); 522 $scripts->add( 'codemirror-addon-hint-xml', "/wp-includes/js/codemirror/addon/hint/xml-hint.js", array( 'codemirror-addon-hint-show', 'codemirror-mode-xml' ), '5.23.0' ); 523 524 $scripts->add( 'codemirror-addon-comment', "/wp-includes/js/codemirror/addon/comment/comment.js", array( 'codemirror' ), '5.23.0' ); 525 $scripts->add( 'codemirror-addon-comment-continuecomment', "/wp-includes/js/codemirror/addon/comment/continuecomment.js", array( 'codemirror' ), '5.23.0' ); 526 527 $scripts->add( 'codemirror-addon-edit-closebrackets', "/wp-includes/js/codemirror/addon/edit/closebrackets.js", array( 'codemirror' ), '5.23.0' ); 528 $scripts->add( 'codemirror-addon-edit-closetag', "/wp-includes/js/codemirror/addon/edit/closetag.js", array( 'codemirror' ), '5.23.0' ); 529 $scripts->add( 'codemirror-addon-edit-continuelist', "/wp-includes/js/codemirror/addon/edit/continuelist.js", array( 'codemirror' ), '5.23.0' ); 530 $scripts->add( 'codemirror-addon-edit-matchbrackets', "/wp-includes/js/codemirror/addon/edit/matchbrackets.js", array( 'codemirror' ), '5.23.0' ); 531 $scripts->add( 'codemirror-addon-edit-matchtags', "/wp-includes/js/codemirror/addon/edit/matchtags.js", array( 'codemirror' ), '5.23.0' ); 532 $scripts->add( 'codemirror-addon-edit-trailingspace', "/wp-includes/js/codemirror/addon/edit/trailingspace.js", array( 'codemirror' ), '5.23.0' ); 533 534 $scripts->add( 'codemirror-addon-selection-active-line', "/wp-includes/js/codemirror/addon/selection/active-line.js", array( 'codemirror' ), '5.23.0' ); 535 $scripts->add( 'codemirror-addon-selection-mark-selection', "/wp-includes/js/codemirror/addon/selection/mark-selection.js", array( 'codemirror' ), '5.23.0' ); 536 $scripts->add( 'codemirror-addon-selection-pointer', "/wp-includes/js/codemirror/addon/selection/selection-pointer.js", array( 'codemirror' ), '5.23.0' ); 537 538 $scripts->add( 'codemirror-mode-clike', "/wp-includes/js/codemirror/mode/clike/clike.js", array( 'codemirror' ), '5.23.0' ); 539 $scripts->add( 'codemirror-mode-css', "/wp-includes/js/codemirror/mode/css/css.js", array( 'codemirror', 540 'codemirror-addon-edit-matchbrackets' ), '5.23.0' ); 541 $scripts->add( 'codemirror-mode-diff', "/wp-includes/js/codemirror/mode/diff/diff.js", array( 'codemirror' ), '5.23.0' ); 542 $scripts->add( 'codemirror-mode-html', "/wp-includes/js/codemirror/mode/htmlmixed/htmlmixed.js", array( 'codemirror' ), '5.23.0' ); 543 $scripts->add( 'codemirror-mode-http', "/wp-includes/js/codemirror/mode/http/http.js", array( 'codemirror' ), '5.23.0' ); 544 $scripts->add( 'codemirror-mode-javascript', "/wp-includes/js/codemirror/mode/javascript/javascript.js", array( 'codemirror' ), '5.23.0' ); 545 $scripts->add( 'codemirror-mode-markdown', "/wp-includes/js/codemirror/mode/markdown/markdown.js", array( 'codemirror' ), '5.23.0' ); 546 $scripts->add( 'codemirror-mode-php', "/wp-includes/js/codemirror/mode/php/php.js", array( 'codemirror-mode-clike', 547 'codemirror-addon-edit-matchbrackets' ), '5.23.0' ); 548 $scripts->add( 'codemirror-mode-shell', "/wp-includes/js/codemirror/mode/shell/shell.js", array( 'codemirror' ), '5.23.0' ); 549 $scripts->add( 'codemirror-mode-sql', "/wp-includes/js/codemirror/mode/sql/sql.js", array( 'codemirror' ), '5.23.0' ); 550 $scripts->add( 'codemirror-mode-xml', "/wp-includes/js/codemirror/mode/xml/xml.js", array( 'codemirror' ), '5.23.0' ); 551 514 552 if ( is_admin() ) { 515 553 $scripts->add( 'admin-tags', "/wp-admin/js/tags$suffix.js", array( 'jquery', 'wp-ajax-response' ), false, 1 ); 516 554 did_action( 'init' ) && $scripts->localize( 'admin-tags', 'tagsl10n', array( … … 874 912 $styles->add( 'mediaelement', "/wp-includes/js/mediaelement/mediaelementplayer.min.css", array(), '2.22.0' ); 875 913 $styles->add( 'wp-mediaelement', "/wp-includes/js/mediaelement/wp-mediaelement$suffix.css", array( 'mediaelement' ) ); 876 914 $styles->add( 'thickbox', '/wp-includes/js/thickbox/thickbox.css', array( 'dashicons' ) ); 915 $styles->add( 'codemirror', "/wp-includes/js/codemirror/lib/codemirror.css", array(), '5.23.0' ); 916 $styles->add( 'codemirror-addon-show-hint', "/wp-includes/js/codemirror/lib/codemirror.css", array( 'codemirror' ), '5.23.0' ); 877 917 878 918 // Deprecated CSS 879 919 $styles->add( 'deprecated-media', "/wp-admin/css/deprecated-media$suffix.css" );