Make WordPress Core

Ticket #12423: 12423.codemirror.unminified.diff

File 12423.codemirror.unminified.diff, 751.2 KB (added by georgestephanis, 8 years ago)

First patch at including CodeMirror.

  • src/wp-admin/css/common.css

     
    22582258        visibility: visible;
    22592259}
    22602260
    2261 #template div {
     2261#template > div {
    22622262        margin-right: 190px;
    22632263}
    22642264
  • src/wp-admin/theme-editor.php

     
    106106validate_file_to_edit( $file, $allowed_files );
    107107$scrollto = isset( $_REQUEST['scrollto'] ) ? (int) $_REQUEST['scrollto'] : 0;
    108108
     109$codemirror_opts = false;
     110switch ( @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
    109148switch( $action ) {
    110149case 'update':
    111150        check_admin_referer( 'edit-theme_' . $file . $stylesheet );
     
    289328jQuery(document).ready(function($){
    290329        $('#template').submit(function(){ $('#scrollto').val( $('#newcontent').scrollTop() ); });
    291330        $('#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; ?>
    292335});
    293336</script>
    294337<?php
  • src/wp-includes/js/codemirror/AUTHORS

     
     1List of CodeMirror contributors. Updated before every release.
     2
     34r2r
     4Aaron Brooks
     5Abdelouahab
     6Abe Fettig
     7Adam Ahmed
     8Adam King
     9adanlobato
     10Adán Lobato
     11Adrian Aichner
     12Adrian Heine
     13Adrien Bertrand
     14aeroson
     15Ahmad Amireh
     16Ahmad M. Zawawi
     17ahoward
     18Akeksandr Motsjonov
     19Alasdair Smith
     20Alberto González Palomo
     21Alberto Pose
     22Albert Xing
     23Alexander Pavlov
     24Alexander Schepanovski
     25Alexander Shvets
     26Alexander Solovyov
     27Alexandre Bique
     28alexey-k
     29Alex Piggott
     30Aliaksei Chapyzhenka
     31Allen Sarkisyan
     32Amin Shali
     33Amin Ullah Khan
     34amshali@google.com
     35Amsul
     36amuntean
     37Amy
     38Ananya Sen
     39anaran
     40AndersMad
     41Anders Nawroth
     42Anderson Mesquita
     43Anders Wåglund
     44Andrea G
     45Andreas Reischuck
     46Andres Taylor
     47Andre von Houck
     48Andrew Cheng
     49Andrey Fedorov
     50Andrey Klyuchnikov
     51Andrey Lushnikov
     52Andrey Shchekin
     53Andy Joslin
     54Andy Kimball
     55Andy Li
     56Angelo
     57angelozerr
     58angelo.zerr@gmail.com
     59Ankit
     60Ankit Ahuja
     61Ansel Santosa
     62Anthony Dugois
     63anthonygego
     64Anthony Gégo
     65Anthony Grimes
     66Anton Kovalyov
     67Apollo Zhu
     68AQNOUCH Mohammed
     69areos
     70Arnab Bose
     71as3boyan
     72atelierbram
     73AtomicPages LLC
     74Atul Bhouraskar
     75Aurelian Oancea
     76Barret Rennie
     77Basarat Ali Syed
     78Bastian Müller
     79belhaj
     80Bem Jones-Bey
     81benbro
     82Beni Cherniavsky-Paskin
     83Benjamin DeCoste
     84Ben Keen
     85Ben Miller
     86Ben Mosher
     87Bernhard Sirlinger
     88Bert Chang
     89Bharad
     90BigBlueHat
     91Billy Moon
     92binny
     93B Krishna Chaitanya
     94Blaine G
     95blukat29
     96boomyjee
     97borawjm
     98Brad Metcalf
     99Brandon Frohs
     100Brandon Wamboldt
     101Brett Zamir
     102Brian Grinstead
     103Brian Sletten
     104Bruce Mitchener
     105Caitlin Potter
     106Calin Barbat
     107callodacity
     108Camilo Roca
     109Chad Jolly
     110Chandra Sekhar Pydi
     111Charles Skelton
     112Cheah Chu Yeow
     113Chris Coyier
     114Chris Ford
     115Chris Granger
     116Chris Houseknecht
     117Chris Lohfink
     118Chris Morgan
     119Chris Smith
     120Christian Oyarzun
     121Christian Petrov
     122Christopher Brown
     123Christopher Mitchell
     124Christopher Pfohl
     125Chunliang Lyu
     126ciaranj
     127CodeAnimal
     128coderaiser
     129Cole R Lawrence
     130ComFreek
     131Curtis Gagliardi
     132dagsta
     133daines
     134Dale Jung
     135Dan Bentley
     136Dan Heberden
     137Daniel, Dao Quang Minh
     138Daniele Di Sarli
     139Daniel Faust
     140Daniel Huigens
     141Daniel Kesler
     142Daniel KJ
     143Daniel Neel
     144Daniel Parnell
     145Danny Yoo
     146darealshinji
     147Darius Roberts
     148Dave Brondsema
     149Dave Myers
     150David Barnett
     151David H. Bronke
     152David Mignot
     153David Pathakjee
     154David Vázquez
     155David Whittington
     156deebugger
     157Deep Thought
     158Devin Abbott
     159Devon Carew
     160Dick Choi
     161dignifiedquire
     162Dimage Sapelkin
     163Dmitry Kiselyov
     164domagoj412
     165Dominator008
     166Domizio Demichelis
     167Doug Wikle
     168Drew Bratcher
     169Drew Hintz
     170Drew Khoury
     171Drini Cami
     172Dror BG
     173duralog
     174eborden
     175edsharp
     176ekhaled
     177Elisée
     178Emmanuel Schanzer
     179Enam Mijbah Noor
     180Eric Allam
     181Erik Welander
     182eustas
     183Fabien O'Carroll
     184Fabio Zendhi Nagao
     185Faiza Alsaied
     186Fauntleroy
     187fbuchinger
     188feizhang365
     189Felipe Lalanne
     190Felix Raab
     191ficristo
     192Filip Noetzel
     193Filip Stollár
     194flack
     195ForbesLindesay
     196Forbes Lindesay
     197Ford_Lawnmower
     198Forrest Oliphant
     199Frank Wiegand
     200Gabriel Gheorghian
     201Gabriel Horner
     202Gabriel Nahmias
     203galambalazs
     204Gary Sheng
     205Gautam Mehta
     206Gavin Douglas
     207gekkoe
     208geowarin
     209Gerard Braad
     210Gergely Hegykozi
     211Giovanni Calò
     212Glebov Boris
     213Glenn Jorde
     214Glenn Ruehle
     215Golevka
     216Google Inc.
     217Gordon Smith
     218Grant Skinner
     219greengiant
     220Gregory Koberger
     221Grzegorz Mazur
     222Guillaume Massé
     223Guillaume Massé
     224guraga
     225Gustavo Rodrigues
     226Hakan Tunc
     227Hans Engel
     228Hardest
     229Harshvardhan Gupta
     230Hasan Karahan
     231Hector Oswaldo Caballero
     232Hendrik Wallbaum
     233Herculano Campos
     234Hiroyuki Makino
     235hitsthings
     236Hocdoc
     237Hugues Malphettes
     238Ian Beck
     239Ian Dickinson
     240Ian Wehrman
     241Ian Wetherbee
     242Ice White
     243ICHIKAWA, Yuji
     244idleberg
     245ilvalle
     246Ingo Richter
     247Irakli Gozalishvili
     248Ivan Kurnosov
     249Ivoah
     250Jacob Lee
     251Jake Peyser
     252Jakob Miland
     253Jakub Vrana
     254Jakub Vrána
     255James Campos
     256James Howard
     257James Thorne
     258Jamie Hill
     259Jan Jongboom
     260jankeromnes
     261Jan Keromnes
     262Jan Odvarko
     263Jan Schär
     264Jan T. Sott
     265Jared Dean
     266Jared Forsyth
     267Jared Jacobs
     268Jason
     269Jason Barnabe
     270Jason Grout
     271Jason Johnston
     272Jason San Jose
     273Jason Siefken
     274Jaydeep Solanki
     275Jean Boussier
     276Jeff Blaisdell
     277Jeff Jenkins
     278jeffkenton
     279Jeff Pickhardt
     280jem (graphite)
     281Jeremy Parmenter
     282Jim
     283Jim Avery
     284JobJob
     285jochenberger
     286Jochen Berger
     287Joel Einbinder
     288joelpinheiro
     289Johan Ask
     290John Connor
     291John-David Dalton
     292John Engler
     293John Lees-Miller
     294John Snelson
     295John Van Der Loo
     296Jon Ander Peñalba
     297Jonas Döbertin
     298Jonathan Malmaud
     299Jon Gacnik
     300jongalloway
     301Jon Malmaud
     302Jon Sangster
     303Joost-Wim Boekesteijn
     304Joseph Pecoraro
     305Josh Cohen
     306Josh Soref
     307Joshua Newman
     308Josh Watzman
     309jots
     310jsoojeon
     311ju1ius
     312Juan Benavides Romero
     313Jucovschi Constantin
     314Juho Vuori
     315Julien Rebetez
     316Justin Andresen
     317Justin Hileman
     318jwallers@gmail.com
     319kaniga
     320karevn
     321Kayur Patel
     322Kazuhito Hokamura
     323Ken Newman
     324ken restivo
     325Ken Rockot
     326Kevin Earls
     327Kevin Sawicki
     328Kevin Ushey
     329Klaus Silveira
     330Koh Zi Han, Cliff
     331komakino
     332Konstantin Lopuhin
     333koops
     334Kris Ciccarello
     335ks-ifware
     336kubelsmieci
     337KwanEsq
     338Kyle Kelley
     339Lanfei
     340Lanny
     341Laszlo Vidacs
     342leaf corcoran
     343Leonid Khachaturov
     344Leon Sorokin
     345Leonya Khachaturov
     346Liam Newman
     347Libo Cannici
     348LloydMilligan
     349LM
     350lochel
     351Lorenzo Stoakes
     352Luciano Longo
     353Lu Fangjian
     354Luke Browning
     355Luke Granger-Brown
     356Luke Stagner
     357lynschinzer
     358M1cha
     359Madhura Jayaratne
     360Maksim Lin
     361Maksym Taran
     362Malay Majithia
     363Manideep
     364Manuel Rego Casasnovas
     365Marat Dreizin
     366Marcel Gerber
     367Marcelo Camargo
     368Marco Aurélio
     369Marco Munizaga
     370Marcus Bointon
     371Marek Rudnicki
     372Marijn Haverbeke
     373Mário Gonçalves
     374Mario Pietsch
     375Mark Anderson
     376Mark Lentczner
     377Marko Bonaci
     378Mark Peace
     379Markus Bordihn
     380Martin Balek
     381Martín Gaitán
     382Martin Hasoň
     383Martin Hunt
     384Martin Laine
     385Martin Zagora
     386Mason Malone
     387Mateusz Paprocki
     388Mathias Bynens
     389mats cronqvist
     390Matt Gaide
     391Matthew Bauer
     392Matthew Beale
     393matthewhayes
     394Matthew Rathbone
     395Matthias Bussonnier
     396Matthias BUSSONNIER
     397Matt McDonald
     398Matt Pass
     399Matt Sacks
     400mauricio
     401Maximilian Hils
     402Maxim Kraev
     403Max Kirsch
     404Max Schaefer
     405Max Xiantu
     406mbarkhau
     407McBrainy
     408melpon
     409Metatheos
     410Micah Dubinko
     411Michael
     412Michael Goderbauer
     413Michael Grey
     414Michael Kaminsky
     415Michael Lehenbauer
     416Michael Zhou
     417Michal Dorner
     418Mighty Guava
     419Miguel Castillo
     420mihailik
     421Mike
     422Mike Brevoort
     423Mike Diaz
     424Mike Ivanov
     425Mike Kadin
     426Mike Kobit
     427MinRK
     428Miraculix87
     429misfo
     430mkaminsky11
     431mloginov
     432Moritz Schwörer
     433mps
     434ms
     435mtaran-google
     436Mu-An Chiou
     437Narciso Jaramillo
     438Nathan Williams
     439ndr
     440nerbert
     441nextrevision
     442ngn
     443nguillaumin
     444Ng Zhi An
     445Nicholas Bollweg
     446Nicholas Bollweg (Nick)
     447Nick Kreeger
     448Nick Small
     449Nicolò Ribaudo
     450Niels van Groningen
     451nightwing
     452Nikita Beloglazov
     453Nikita Vasilyev
     454Nikolay Kostov
     455nilp0inter
     456Nisarg Jhaveri
     457nlwillia
     458noragrossman
     459Norman Rzepka
     460Oreoluwa Onatemowo
     461Oskar Segersvärd
     462pablo
     463pabloferz
     464Page
     465Panupong Pasupat
     466paris
     467Paris
     468Paris Kasidiaris
     469Patil Arpith
     470Patrick Stoica
     471Patrick Strawderman
     472Paul Garvin
     473Paul Ivanov
     474Paul Masson
     475Pavel
     476Pavel Feldman
     477Pavel Petržela
     478Pavel Strashkin
     479Paweł Bartkiewicz
     480peteguhl
     481peter
     482Peter Flynn
     483peterkroon
     484Peter Kroon
     485Philipp A
     486Philip Stadermann
     487Pierre Gerold
     488Piët Delport
     489Pontus Melke
     490prasanthj
     491Prasanth J
     492Prayag Verma
     493Radek Piórkowski
     494Rahul
     495Rahul Anand
     496ramwin1
     497Randall Mason
     498Randy Burden
     499Randy Edmunds
     500Rasmus Erik Voel Jensen
     501ray ratchup
     502Ray Ratchup
     503Remi Nyborg
     504Richard Denton
     505Richard van der Meer
     506Richard Z.H. Wang
     507Rishi Goomar
     508Robert Crossfield
     509Roberto Abdelkader Martínez Pérez
     510robertop23
     511Robert Plummer
     512Rrandom
     513Ruslan Osmanov
     514Ryan Prior
     515sabaca
     516Sam Lee
     517Samuel Ainsworth
     518Sam Wilson
     519sandeepshetty
     520Sander AKA Redsandro
     521Sander Verweij
     522santec
     523Sascha Peilicke
     524satamas
     525satchmorun
     526sathyamoorthi
     527S. Chris Colbert
     528SCLINIC\jdecker
     529Scott Aikin
     530Scott Goodhew
     531Sebastian Zaha
     532Sergey Goder
     533Sergey Tselovalnikov
     534Se-Won Kim
     535shaund
     536shaun gilchrist
     537Shawn A
     538Shea Bunge
     539sheopory
     540Shiv Deepak
     541Shmuel Englard
     542Shubham Jain
     543Siamak Mokhtari
     544silverwind
     545sinkuu
     546snasa
     547soliton4
     548sonson
     549spastorelli
     550srajanpaliwal
     551Stanislav Oaserele
     552Stas Kobzar
     553Stefan Borsje
     554Steffen Beyer
     555Steffen Bruchmann
     556Stephen Lavelle
     557Steve Champagne
     558Steve Hoover
     559Steve O'Hara
     560stoskov
     561Stu Kennedy
     562Sungho Kim
     563sverweij
     564Taha Jahangir
     565takamori
     566Tako Schotanus
     567Takuji Shimokawa
     568Tarmil
     569TDaglis
     570tel
     571tfjgeorge
     572Thaddee Tyl
     573thanasis
     574TheHowl
     575themrmax
     576think
     577Thomas Dvornik
     578Thomas Kluyver
     579Thomas Schmid
     580Tim Alby
     581Tim Baumann
     582Timothy Farrell
     583Timothy Gu
     584Timothy Hatcher
     585TobiasBg
     586Todd Berman
     587Tomas-A
     588Tomas Varaneckas
     589Tom Erik Støwer
     590Tom Klancer
     591Tom MacWright
     592Tony Jian
     593Travis Heppe
     594Triangle717
     595Tristan Tarrant
     596TSUYUSATO Kitsune
     597twifkak
     598VapidWorx
     599Vestimir Markov
     600vf
     601Victor Bocharsky
     602Vincent Woo
     603Volker Mische
     604Weiyan Shao
     605wenli
     606Wes Cossick
     607Wesley Wiser
     608Will Binns-Smith
     609Will Dean
     610William Jamieson
     611William Stein
     612Willy
     613Wojtek Ptak
     614Wu Cheng-Han
     615Xavier Mendez
     616Yassin N. Hassan
     617YNH Webdev
     618Yunchi Luo
     619Yuvi Panda
     620Zac Anger
     621Zachary Dremann
     622Zeno Rocha
     623Zhang Hao
     624zziuni
     625魏鹏刚
  • src/wp-includes/js/codemirror/CHANGELOG.md

     
     1## 5.23.0 (2017-01-19)
     2
     3### Bug fixes
     4
     5Presentation-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
     21Include 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
     31A 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
     39New event: [`optionChange`](http://codemirror.net/doc/manual.html#event_optionChange).
     40
     41## 5.21.0 (2016-11-21)
     42
     43### Bug fixes
     44
     45Tapping/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
     47Fix various crashes and misbehaviors when reading composition events in [contentEditable mode](http://codemirror.net/doc/manual.html#option_inputStyle).
     48
     49Catches 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
     63The [`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
     65The [`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
     75Fix `CodeMirror.version` returning the wrong version number.
     76
     77## 5.20.0 (2016-10-20)
     78
     79### Bug fixes
     80
     81Make `newlineAndIndent` command work with multiple cursors on the same line.
     82
     83Make sure keypress events for backspace are ignored.
     84
     85Tokens styled with overlays no longer get a nonsense `cm-cm-overlay` class.
     86
     87Line 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
     97The 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
     99The [`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
     109xml-fold addon: Fix a null-dereference bug.
     110
     111Page up and page down now do something even in single-line documents.
     112
     113Fix 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
     119The [`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
     131Make sure [gutter backgrounds](http://codemirror.net/doc/manual.html#addLineClass) stick to the rest of the gutter during horizontal scrolling.
     132
     133The 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
     145The [`addOverlay`](http://codemirror.net/doc/manual.html#addOverlay) method now supports a `priority` option to control the order in which overlays are applied.
     146
     147MIME types that end in `+json` now default to the JSON mode when the MIME itself is not defined.
     148
     149### Breaking changes
     150
     151The mode formerly known as Jade was renamed to [Pug](http://codemirror.net/mode/pug).
     152
     153The [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
     159Fix problem with wrapped trailing whitespace displaying incorrectly.
     160
     161Prevent IME dialog from overlapping typed content in Chrome.
     162
     163Improve 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
     179Fix glitches when dragging content caused by the drop indicator receiving mouse events.
     180
     181Make Control-drag work on Firefox.
     182
     183Make 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
     199Fix 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
     205Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode.
     206
     207Fix issue where not all ASCII control characters were being replaced by placeholders.
     208
     209Remove the assumption that all modes have a `startState` method from several wrapping modes.
     210
     211Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any.
     212
     213Optimize 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
     221Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected.
     222
     223Pasting [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
     231Push 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
     233Set `dataTransfer.effectAllowed` in `dragstart` handler to help browsers use the right drag icon.
     234
     235Add 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
     271Solves 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
     277New 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
     283Fix problem where [`findMarks`](http://codemirror.net/doc/manual.html#findMarks) sometimes failed to find multi-line marks.
     284
     285Fix 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
     317Fix [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
     319Use 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
     321Solve a [problem](https://github.com/codemirror/CodeMirror/issues/3821) where the horizontal scrollbar could hide text in Firefox.
     322
     323Fix 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
     470Unfortunately, 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
     474Emergency 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
     566Merges 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
     613Emergency 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
     685Single-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
     727Integrate 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
     785Emergency 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
     878Fixes `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
     906Fix 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
     952Add 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
     984CodeMirror 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
     9Community discussion, questions, and informal bug reporting is done on the
     10[discuss.CodeMirror forum](http://discuss.codemirror.net).
     11
     12## Submitting bug reports
     13
     14The preferred way to report bugs is to use the
     15[GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before
     16reporting a bug, read these pointers.
     17
     18**Note:** The issue tracker is for *bugs*, not requests for help. Questions
     19should 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
     67By 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

     
     1Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
     2
     3Permission is hereby granted, free of charge, to any person obtaining a copy
     4of this software and associated documentation files (the "Software"), to deal
     5in the Software without restriction, including without limitation the rights
     6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7copies of the Software, and to permit persons to whom the Software is
     8furnished to do so, subject to the following conditions:
     9
     10The above copyright notice and this permission notice shall be included in
     11all copies or substantial portions of the Software.
     12
     13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     19THE 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
     33li.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
     142div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
     143div.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
     303div.CodeMirror-cursors {
     304  visibility: hidden;
     305  position: relative;
     306  z-index: 3;
     307}
     308div.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 */
     341span.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.
     18var userAgent = navigator.userAgent
     19var platform = navigator.platform
     20
     21var gecko = /gecko\/\d/i.test(userAgent)
     22var ie_upto10 = /MSIE \d/.test(userAgent)
     23var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent)
     24var ie = ie_upto10 || ie_11up
     25var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1])
     26var webkit = /WebKit\//.test(userAgent)
     27var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent)
     28var chrome = /Chrome\//.test(userAgent)
     29var presto = /Opera\//.test(userAgent)
     30var safari = /Apple Computer/.test(navigator.vendor)
     31var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent)
     32var phantom = /PhantomJS/.test(userAgent)
     33
     34var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent)
     35// This is woefully incomplete. Suggestions for alternative methods welcome.
     36var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent)
     37var mac = ios || /Mac/.test(platform)
     38var chromeOS = /\bCrOS\b/.test(userAgent)
     39var windows = /win/i.test(platform)
     40
     41var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/)
     42if (presto_version) { presto_version = Number(presto_version[1]) }
     43if (presto_version && presto_version >= 15) { presto = false; webkit = true }
     44// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
     45var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11))
     46var captureRightClick = gecko || (ie && ie_version >= 9)
     47
     48function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
     49
     50var 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
     59function removeChildren(e) {
     60  for (var count = e.childNodes.length; count > 0; --count)
     61    { e.removeChild(e.firstChild) }
     62  return e
     63}
     64
     65function removeChildrenAndAdd(parent, e) {
     66  return removeChildren(parent).appendChild(e)
     67}
     68
     69function 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
     78var range
     79if (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} }
     85else { 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
     95function 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
     106function 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
     121function addClass(node, cls) {
     122  var current = node.className
     123  if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls }
     124}
     125function 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
     132var selectInput = function(node) { node.select() }
     133if (ios) // Mobile Safari apparently has a bug where select() is broken.
     134  { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } }
     135else if (ie) // Suppress mysterious IE10 errors
     136  { selectInput = function(node) { try { node.select() } catch(_e) {} } }
     137
     138function bind(f) {
     139  var args = Array.prototype.slice.call(arguments, 1)
     140  return function(){return f.apply(null, args)}
     141}
     142
     143function 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.
     153function 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
     168function Delayed() {this.id = null}
     169Delayed.prototype.set = function(ms, f) {
     170  clearTimeout(this.id)
     171  this.id = setTimeout(f, ms)
     172}
     173
     174function 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
     181var scrollerGap = 30
     182
     183// Returned or thrown by various protocols to signal 'I'm not
     184// handling this'.
     185var Pass = {toString: function(){return "CodeMirror.Pass"}}
     186
     187// Reused option objects for setSelection & friends
     188var sel_dontScroll = {scroll: false};
     189var sel_mouse = {origin: "*mouse"};
     190var sel_move = {origin: "+move"};
     191// The inverse of countColumn -- find the offset that corresponds to
     192// a particular column.
     193function 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
     207var spaceStrs = [""]
     208function spaceStr(n) {
     209  while (spaceStrs.length <= n)
     210    { spaceStrs.push(lst(spaceStrs) + " ") }
     211  return spaceStrs[n]
     212}
     213
     214function lst(arr) { return arr[arr.length-1] }
     215
     216function 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
     222function 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
     228function nothing() {}
     229
     230function 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
     242var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/
     243function isWordCharBasic(ch) {
     244  return /\w/.test(ch) || ch > "\x80" &&
     245    (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
     246}
     247function 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
     253function 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.
     263var 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]/
     264function 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
     270function 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.
     369function 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.
     385function 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.
     397function 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.
     405function 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.
     412function 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.
     426function 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
     446function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
     447
     448function lineNumberFor(options, i) {
     449  return String(options.lineNumberFormatter(i + options.firstLineNumber))
     450}
     451
     452// A Pos instance represents a position within the text.
     453function 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.
     460function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
     461
     462function copyPos(x) {return Pos(x.line, x.ch)}
     463function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
     464function 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.
     468function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
     469function 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}
     475function 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}
     481function 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.
     488var sawReadOnlySpans = false;
     489var sawCollapsedSpans = false;
     490function seeReadOnlySpans() {
     491  sawReadOnlySpans = true
     492}
     493
     494function seeCollapsedSpans() {
     495  sawCollapsedSpans = true
     496}
     497
     498// TEXTMARKER SPANS
     499
     500function 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.
     506function 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).
     514function 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.
     521function 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).
     530function 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}
     542function 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.
     562function 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.
     624function 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.
     635function 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.
     664function 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}
     671function 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.
     680function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
     681function 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.
     686function 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.
     699function 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}
     709function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
     710function 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.
     715function 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).
     735function 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.
     744function 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.
     755function 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.
     763function 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.
     775function 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}
     786function 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.
     803function 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.
     825function 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.
     844function 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
     860function 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
     873function bidiLeft(part) { return part.level % 2 ? part.to : part.from }
     874function bidiRight(part) { return part.level % 2 ? part.from : part.to }
     875
     876function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 }
     877function lineRight(line) {
     878  var order = getOrder(line)
     879  if (!order) { return line.text.length }
     880  return bidiRight(lst(order))
     881}
     882
     883function 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
     890var bidiOther = null
     891function 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
     912function 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.
     924function 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
     947function 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.
     976var 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.
     1128function 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
     1139var noHandlers = []
     1140
     1141var 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
     1152function getHandlers(emitter, type) {
     1153  return emitter._handlers && emitter._handlers[type] || noHandlers
     1154}
     1155
     1156function 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
     1171function 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.
     1181function 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
     1188function 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
     1196function 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.
     1202function 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
     1210function e_preventDefault(e) {
     1211  if (e.preventDefault) { e.preventDefault() }
     1212  else { e.returnValue = false }
     1213}
     1214function e_stopPropagation(e) {
     1215  if (e.stopPropagation) { e.stopPropagation() }
     1216  else { e.cancelBubble = true }
     1217}
     1218function e_defaultPrevented(e) {
     1219  return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
     1220}
     1221function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)}
     1222
     1223function e_target(e) {return e.target || e.srcElement}
     1224function 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
     1236var 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
     1244var zwspSupported
     1245function 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
     1259var badBidiRects
     1260function 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.
     1272var 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
     1290var 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
     1301var 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
     1308var badZoomedRects = null
     1309function 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
     1317var modes = {};
     1318var 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.)
     1322function defineMode(name, mode) {
     1323  if (arguments.length > 2)
     1324    { mode.dependencies = Array.prototype.slice.call(arguments, 2) }
     1325  modes[name] = mode
     1326}
     1327
     1328function 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.
     1334function 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.
     1353function 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.
     1376var modeExtensions = {}
     1377function extendMode(mode, properties) {
     1378  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
     1379  copyObj(properties, exts)
     1380}
     1381
     1382function 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.
     1396function 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
     1407function 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
     1416var 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
     1424StringStream.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.
     1495function 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
     1534function 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
     1547function 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.
     1566function 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
     1577function 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
     1584function 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
     1594function 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
     1615function 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.
     1630function 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.
     1674function 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).
     1694function Line(text, markedSpans, estimateHeight) {
     1695  this.text = text
     1696  attachMarkedSpans(this, markedSpans)
     1697  this.height = estimateHeight ? estimateHeight(this) : 1
     1698}
     1699eventMixin(Line)
     1700Line.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.
     1705function 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.
     1717function 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.
     1725var styleToClassCache = {};
     1726var styleToClassCacheWithMode = {};
     1727function 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.
     1739function 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
     1800function 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.
     1809function 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
     1871function 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.
     1886function 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
     1906function 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.
     1924function 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.
     1996function 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.
     2008function 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
     2018var operationGroup = null
     2019
     2020function 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
     2031function 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
     2047function 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
     2058var 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.
     2067function 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
     2087function 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.
     2096function 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
     2109function 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
     2120function 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.
     2134function 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.
     2147function 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
     2162function 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
     2172function 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
     2209function 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
     2220function 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.
     2234function 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
     2240function 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
     2256function 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
     2274function 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
     2290function 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
     2300function paddingTop(display) {return display.lineSpace.offsetTop}
     2301function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
     2302function 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
     2311function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
     2312function displayWidth(cm) {
     2313  return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
     2314}
     2315function 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.
     2323function 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.)
     2344function 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.
     2357function 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.
     2370function 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.
     2375function 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.
     2388function 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).
     2410function 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
     2430var nullRect = {left: 0, right: 0, top: 0, bottom: 0}
     2431
     2432function 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
     2470function 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
     2480function 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.
     2533function 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
     2543function 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
     2552function 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
     2559function 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
     2566function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft }
     2567function 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".
     2573function 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.
     2595function 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
     2612function 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.
     2620function 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.
     2652function 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.
     2667function 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).
     2676function 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
     2697function 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
     2750var measureText
     2751// Compute the default text height.
     2752function 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.
     2772function 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.
     2784function 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.
     2801function 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).
     2808function 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
     2826function 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.
     2839function 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.
     2857function 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
     2868function updateSelection(cm) {
     2869  cm.display.input.showSelection(cm.display.input.prepareSelection())
     2870}
     2871
     2872function 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
     2891function 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
     2910function 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
     2983function 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
     2996function ensureFocus(cm) {
     2997  if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) }
     2998}
     2999
     3000function 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
     3008function 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}
     3027function 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.
     3041function 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.
     3064function 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.
     3084function 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.
     3111function 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.
     3119function 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.
     3142function 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.
     3153function 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
     3173var wheelSamples = 0;
     3174var 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).
     3179if (ie) { wheelPixelsPerUnit = -.53 }
     3180else if (gecko) { wheelPixelsPerUnit = 15 }
     3181else if (chrome) { wheelPixelsPerUnit = -.7 }
     3182else if (safari) { wheelPixelsPerUnit = -1/3 }
     3183
     3184function 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}
     3191function wheelEventPixels(e) {
     3192  var delta = wheelEventDelta(e)
     3193  delta.x *= wheelPixelsPerUnit
     3194  delta.y *= wheelPixelsPerUnit
     3195  return delta
     3196}
     3197
     3198function 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.
     3277function 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
     3293var 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
     3311NativeScrollbars.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
     3348NativeScrollbars.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
     3353NativeScrollbars.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
     3358NativeScrollbars.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
     3366NativeScrollbars.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
     3383NativeScrollbars.prototype.clear = function () {
     3384  var parent = this.horiz.parentNode
     3385  parent.removeChild(this.horiz)
     3386  parent.removeChild(this.vert)
     3387};
     3388
     3389var NullScrollbars = function () {};
     3390
     3391NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
     3392NullScrollbars.prototype.setScrollLeft = function () {};
     3393NullScrollbars.prototype.setScrollTop = function () {};
     3394NullScrollbars.prototype.clear = function () {};
     3395
     3396function 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.
     3410function 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
     3430var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}
     3431
     3432function 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.
     3458function 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).
     3475function 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).
     3501function 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.
     3511function 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).
     3541function 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.
     3551function 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.
     3565function 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
     3584var nextOpId = 0
     3585// Start a new operation.
     3586function 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
     3608function 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.
     3619function 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
     3633function 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
     3646function endOperation_W1(op) {
     3647  op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update)
     3648}
     3649
     3650function 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
     3671function 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
     3696function 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
     3743function 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.
     3750function 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.
     3760function 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}
     3768function 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.
     3784function 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"
     3849function 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.
     3863function resetView(cm) {
     3864  cm.display.viewFrom = cm.display.viewTo = cm.doc.first
     3865  cm.display.view = []
     3866  cm.display.viewOffset = 0
     3867}
     3868
     3869function 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.
     3896function 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).
     3917function 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
     3928function 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
     3933function 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
     3973var 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
     3988DisplayUpdate.prototype.signal = function (emitter, type) {
     3989  if (hasHandler(emitter, type))
     3990    { this.events.push(arguments) }
     3991};
     3992DisplayUpdate.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
     3999function 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.
     4013function 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
     4085function 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
     4114function 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.
     4131function 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
     4173function updateGutterSpace(cm) {
     4174  var width = cm.display.gutters.offsetWidth
     4175  cm.display.sizer.style.marginLeft = width + "px"
     4176}
     4177
     4178function 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.
     4186function 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.
     4204function 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).
     4219function Selection(ranges, primIndex) {
     4220  this.ranges = ranges
     4221  this.primIndex = primIndex
     4222}
     4223
     4224Selection.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
     4265function Range(anchor, head) {
     4266  this.anchor = anchor; this.head = head
     4267}
     4268
     4269Range.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).
     4280function 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
     4296function 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).
     4302function 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.
     4310function 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
     4319function 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
     4329function 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".
     4338function 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
     4359function loadMode(cm) {
     4360  cm.doc.mode = getMode(cm.options, cm.doc.modeOption)
     4361  resetModeState(cm)
     4362}
     4363
     4364function 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.
     4380function 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.
     4386function 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.
     4438function 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.
     4453function 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
     4464function 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.
     4481function 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.
     4490function 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.
     4500function 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.
     4515function 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
     4558function 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).
     4571function 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
     4593function 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.
     4600function 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.
     4611function 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.
     4622function 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).
     4635function 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
     4659function 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.
     4691function 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.
     4710function 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)
     4716function 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.
     4725function 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.
     4732function 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.
     4738function 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
     4757function 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.
     4768function setSelection(doc, sel, options) {
     4769  setSelectionNoUndo(doc, sel, options)
     4770  addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options)
     4771}
     4772
     4773function 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
     4785function 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.
     4799function 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.
     4805function 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
     4820function 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.
     4853function 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
     4866function 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
     4878function 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
     4885function 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.
     4909function 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
     4931function 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.
     4949function 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.
     5022function 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).
     5038function 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.
     5071function 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
     5130function 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
     5139function 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.
     5155function 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
     5183function 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.
     5192function 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
     5214function 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
     5227LeafChunk.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
     5263function 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
     5278BranchChunk.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
     5376function 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}
     5384eventMixin(LineWidget)
     5385
     5386function adjustScrollWhenAboveVisible(cm, line, diff) {
     5387  if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
     5388    { addToScrollPos(cm, null, diff) }
     5389}
     5390
     5391LineWidget.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}
     5405LineWidget.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
     5417function 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).
     5452var nextMarkerId = 0
     5453
     5454function TextMarker(doc, type) {
     5455  this.lines = []
     5456  this.type = type
     5457  this.doc = doc
     5458  this.id = ++nextMarkerId
     5459}
     5460eventMixin(TextMarker)
     5461
     5462// Clear the marker.
     5463TextMarker.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).
     5512TextMarker.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.
     5534TextMarker.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
     5555TextMarker.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}
     5563TextMarker.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
     5572function 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.
     5647function 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}
     5655eventMixin(SharedTextMarker)
     5656
     5657SharedTextMarker.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}
     5666SharedTextMarker.prototype.find = function(side, lineObj) {
     5667  return this.primary.find(side, lineObj)
     5668}
     5669
     5670function 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
     5685function findSharedMarkers(doc) {
     5686  return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
     5687}
     5688
     5689function 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
     5701function 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
     5717var nextDocId = 0
     5718var 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
     5741Doc.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.
     6127Doc.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)
     6131var lastDrop = 0
     6132
     6133function 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
     6193function 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
     6216function 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
     6228function 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
     6239function 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
     6248var globalsRegistered = false
     6249function ensureGlobalHandlers() {
     6250  if (globalsRegistered) { return }
     6251  registerGlobalHandlers()
     6252  globalsRegistered = true
     6253}
     6254function 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
     6267function 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
     6277var 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
     6289for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
     6290// Alphabetic keys
     6291for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1) }
     6292// Function keys
     6293for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2 }
     6294
     6295var keyMap = {}
     6296
     6297keyMap.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.
     6308keyMap.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.
     6319keyMap.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}
     6326keyMap.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}
     6336keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault
     6337
     6338// KEYMAP DISPATCH
     6339
     6340function 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.
     6364function 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
     6391function 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.
     6410function 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.
     6416function 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
     6427function 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.
     6433function 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.
     6458var 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
     6599function 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}
     6607function 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}
     6617function 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.
     6630function 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
     6650function 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
     6659var stopSeq = new Delayed
     6660function 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.
     6692function 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
     6711function handleCharBinding(cm, e, ch) {
     6712  return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
     6713}
     6714
     6715var lastStoppedKey = null
     6716function 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
     6737function 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
     6752function onKeyUp(e) {
     6753  if (e.keyCode == 16) { this.doc.sel.shift = false }
     6754  signalDOMEvent(this, e)
     6755}
     6756
     6757function 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.
     6775function 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
     6817var lastClick;
     6818var lastDoubleClick;
     6819function 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.
     6846function 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.
     6875function 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.
     7023function 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
     7047function 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.
     7056function 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
     7062function contextMenuInGutter(cm, e) {
     7063  if (!hasHandler(cm, "gutterContextMenu")) { return false }
     7064  return gutterEvent(cm, e, "gutterContextMenu", false)
     7065}
     7066
     7067function 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
     7073var Init = {toString: function(){return "CodeMirror.Init"}}
     7074
     7075var defaults = {}
     7076var optionHandlers = {}
     7077
     7078function 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
     7211function guttersChanged(cm) {
     7212  updateGutters(cm)
     7213  regChange(cm)
     7214  alignHorizontally(cm)
     7215}
     7216
     7217function 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
     7230function 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
     7248function 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.
     7319CodeMirror.defaults = defaults
     7320// Functions to run when options are changed.
     7321CodeMirror.optionHandlers = optionHandlers
     7322
     7323// Attach the necessary event handlers when initializing the editor
     7324function 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
     7431var initHooks = []
     7432CodeMirror.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.
     7439function 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.
     7501var lastCopied = null
     7502
     7503function setLastCopied(newLastCopied) {
     7504  lastCopied = newLastCopied
     7505}
     7506
     7507function 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
     7555function 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
     7565function 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
     7589function 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
     7600function disableBrowserMagic(field, spellcheck) {
     7601  field.setAttribute("autocorrect", "off")
     7602  field.setAttribute("autocapitalize", "off")
     7603  field.setAttribute("spellcheck", !!spellcheck)
     7604}
     7605
     7606function 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
     7629function 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.
     8078function 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.
     8130function 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
     8152var 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
     8161ContentEditableInput.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
     8238ContentEditableInput.prototype.prepareSelection = function () {
     8239  var result = prepareSelection(this.cm, false)
     8240  result.focus = this.cm.state.focused
     8241  return result
     8242};
     8243
     8244ContentEditableInput.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
     8250ContentEditableInput.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
     8293ContentEditableInput.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
     8304ContentEditableInput.prototype.showMultipleSelections = function (info) {
     8305  removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
     8306  removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
     8307};
     8308
     8309ContentEditableInput.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
     8315ContentEditableInput.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
     8322ContentEditableInput.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};
     8329ContentEditableInput.prototype.blur = function () { this.div.blur() };
     8330ContentEditableInput.prototype.getField = function () { return this.div };
     8331
     8332ContentEditableInput.prototype.supportsTouch = function () { return true };
     8333
     8334ContentEditableInput.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
     8350ContentEditableInput.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
     8356ContentEditableInput.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
     8369ContentEditableInput.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
     8432ContentEditableInput.prototype.ensurePolled = function () {
     8433  this.forceCompositionEnd()
     8434};
     8435ContentEditableInput.prototype.reset = function () {
     8436  this.forceCompositionEnd()
     8437};
     8438ContentEditableInput.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};
     8446ContentEditableInput.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
     8461ContentEditableInput.prototype.setUneditable = function (node) {
     8462  node.contentEditable = "false"
     8463};
     8464
     8465ContentEditableInput.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
     8471ContentEditableInput.prototype.readOnlyChanged = function (val) {
     8472  this.div.contentEditable = String(val != "nocursor")
     8473};
     8474
     8475ContentEditableInput.prototype.onContextMenu = function () {};
     8476ContentEditableInput.prototype.resetPosition = function () {};
     8477
     8478ContentEditableInput.prototype.needsContentAttribute = true
     8479
     8480function 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
     8496function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
     8497
     8498function 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
     8539function 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
     8558function 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
     8615var 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
     8634TextareaInput.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
     8717TextareaInput.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
     8735TextareaInput.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)
     8747TextareaInput.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
     8766TextareaInput.prototype.getField = function () { return this.textarea };
     8767
     8768TextareaInput.prototype.supportsTouch = function () { return false };
     8769
     8770TextareaInput.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
     8777TextareaInput.prototype.blur = function () { this.textarea.blur() };
     8778
     8779TextareaInput.prototype.resetPosition = function () {
     8780  this.wrapper.style.top = this.wrapper.style.left = 0
     8781};
     8782
     8783TextareaInput.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.
     8787TextareaInput.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.
     8800TextareaInput.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).
     8817TextareaInput.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
     8868TextareaInput.prototype.ensurePolled = function () {
     8869  if (this.pollingFast && this.poll()) { this.pollingFast = false }
     8870};
     8871
     8872TextareaInput.prototype.onKeyPress = function () {
     8873  if (ie && ie_version >= 9) { this.hasSelection = null }
     8874  this.fastPoll()
     8875};
     8876
     8877TextareaInput.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
     8952TextareaInput.prototype.readOnlyChanged = function (val) {
     8953  if (!val) { this.reset() }
     8954};
     8955
     8956TextareaInput.prototype.setUneditable = function () {};
     8957
     8958TextareaInput.prototype.needsContentAttribute = false
     8959
     8960function 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
     9017function 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
     9063defineOptions(CodeMirror)
     9064
     9065addEditorMethods(CodeMirror)
     9066
     9067// Set up methods on CodeMirror's prototype to redirect to the editor's document.
     9068var dontDelegate = "iter insert remove copy getEditor constructor".split(" ")
     9069for (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
     9074eventMixin(Doc)
     9075
     9076// INPUT HANDLING
     9077
     9078CodeMirror.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.)
     9085CodeMirror.defineMode = function(name/*, mode, …*/) {
     9086  if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name }
     9087  defineMode.apply(this, arguments)
     9088}
     9089
     9090CodeMirror.defineMIME = defineMIME
     9091
     9092// Minimal default mode.
     9093CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); })
     9094CodeMirror.defineMIME("text/plain", "null")
     9095
     9096// EXTENSIONS
     9097
     9098CodeMirror.defineExtension = function (name, func) {
     9099  CodeMirror.prototype[name] = func
     9100}
     9101CodeMirror.defineDocExtension = function (name, func) {
     9102  Doc.prototype[name] = func
     9103}
     9104
     9105CodeMirror.fromTextArea = fromTextArea
     9106
     9107addLegacyProps(CodeMirror)
     9108
     9109CodeMirror.version = "5.23.0"
     9110
     9111return 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
     14function 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}
     22function 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}
     28function 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
     35function 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
     41function 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
     49CodeMirror.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
     14CodeMirror.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
     14CodeMirror.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
     45CodeMirror.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

     
     1s// 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
     14CodeMirror.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
     111CodeMirror.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
     14function 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
     19CodeMirror.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
     784CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
     785
     786CodeMirror.defineMIME("text/javascript", "javascript");
     787CodeMirror.defineMIME("text/ecmascript", "javascript");
     788CodeMirror.defineMIME("application/javascript", "javascript");
     789CodeMirror.defineMIME("application/x-javascript", "javascript");
     790CodeMirror.defineMIME("application/ecmascript", "javascript");
     791CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
     792CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
     793CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
     794CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
     795CodeMirror.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
     14CodeMirror.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
     811CodeMirror.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
     14CodeMirror.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
     138CodeMirror.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
     14CodeMirror.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
     14var 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
     48var xmlConfig = {
     49  autoSelfClosers: {},
     50  implicitlyClosed: {},
     51  contextGrabbers: {},
     52  doNotIndent: {},
     53  allowUnquoted: false,
     54  allowMissing: false,
     55  caseFold: false
     56}
     57
     58CodeMirror.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
     389CodeMirror.defineMIME("text/xml", "xml");
     390CodeMirror.defineMIME("application/xml", "xml");
     391if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
     392  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
     393
     394});
  • src/wp-includes/script-loader.php

     
    511511                'versionString' => 'wp/v2/',
    512512        ) );
    513513
     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
    514552        if ( is_admin() ) {
    515553                $scripts->add( 'admin-tags', "/wp-admin/js/tags$suffix.js", array( 'jquery', 'wp-ajax-response' ), false, 1 );
    516554                did_action( 'init' ) && $scripts->localize( 'admin-tags', 'tagsl10n', array(
     
    874912        $styles->add( 'mediaelement',        "/wp-includes/js/mediaelement/mediaelementplayer.min.css", array(), '2.22.0' );
    875913        $styles->add( 'wp-mediaelement',     "/wp-includes/js/mediaelement/wp-mediaelement$suffix.css", array( 'mediaelement' ) );
    876914        $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' );
    877917
    878918        // Deprecated CSS
    879919        $styles->add( 'deprecated-media', "/wp-admin/css/deprecated-media$suffix.css" );