Full Code of jacopocolo/p5-sketchplugin for AI

master 51faaf6d5ec8 cached
19 files
509.9 KB
138.5k tokens
513 symbols
1 requests
Download .txt
Showing preview only (526K chars total). Download the full file or copy to clipboard to get everything.
Repository: jacopocolo/p5-sketchplugin
Branch: master
Commit: 51faaf6d5ec8
Files: 19
Total size: 509.9 KB

Directory structure:
gitextract_kg813njs/

├── .gitignore
├── LICENSE
├── README.md
├── appcast.xml
├── full-reference.md
├── p5.sketchplugin/
│   └── Contents/
│       ├── Resources/
│       │   ├── codemirror.css
│       │   ├── codemirror.js
│       │   ├── editor.html
│       │   ├── javascript.js
│       │   └── p5-light.css
│       └── Sketch/
│           ├── 2d_primitives.js
│           ├── MochaJSDelegate.js
│           ├── array_functions.js
│           ├── conversion.js
│           ├── manifest.json
│           ├── notsupported.js
│           ├── p5.js
│           └── utils.js
└── reference.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
~$appcast.xml


================================================
FILE: LICENSE
================================================
                  GNU LESSER GENERAL PUBLIC LICENSE
                       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

(This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.)

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

                  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

                     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    {description}
    Copyright (C) {year} {fullname}

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
    USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random
  Hacker.

  {signature of Ty Coon}, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!



================================================
FILE: README.md
================================================
# p5.sketchplugin (⚠️ not under active development anymore)
### Create rich and editable graphics inside Sketch using only few lines of JavaScript

![p5.sketchplugin](https://github.com/jacopocolo/p5-sketchplugin/blob/master/img/Preview@2x.png)

p5.sketchplugin is a plugin for running some [p5.js](http://p5js.org/) code inside Sketch. You write simple JavaScript — using the powerful p5.js functions — and it creates editable layers in a new artobard.

After you run the code, all the functions you called are rendered as native Sketch objects: this means that you can tweak every single object you create when you are done. Your code will rendered in the same artboard every time your run it. If you want to keep a copy, duplicate the artboard.

Read more at: [jacopocolo.com/p5sketchplugin/](http://www.jacopocolo.com/p5sketchplugin/)

## Installation

### [Download the latest version here](https://github.com/jacopocolo/p5-sketchplugin/releases)

## Limitations

p5.sketchplugin doesn't match the whole p5.js library but it gives you access to most of the basic functions for drawing on an artboard. Take a look at the [reference](#reference) below to see what is available and how to call these functions.

The vast majority of the functions implemented match the behaviour available in p5.js. You can copy and paste code from the [p5.js website examples](https://p5js.org/examples/) and in most cases it will simply work. If it doesn't, take a look at the [debugging](#debugging-your-code) section for tips on how to adjust your code.

A warning: you are about to execute code inside Sketch. The plugin is designed to act only on a single artboard and tested heavily, but bugs and broken code could still be dangerous. Save your work before using this plugin.

## Reference

- [Color](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#color)
  * Settings
    + [background()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#backgroundcolor)
    + [fill()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#fillcolor)
    + [noFill()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#nofill)
    + [noStroke()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#nostroke)
    + [stroke()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#strokecolor)
- [Shape](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#shape)
  * 2D Primitives
    + [arc()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#arcxywhstartstop)
    + [ellipse()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#ellipsexywh)
    + [line()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#linex1y1x2y2)
    + [point()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#pointx1y1)
    + [quad()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#quadx1y1x2y2x3y3x4y4)
    + [rect()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#rectxywh)
    + [triangle()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#trianglex1y1x2y2x3y3)
  * Attributes
    + [strokeCap()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#strokecapcap)
    + [strokeJoin()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#strokejoinjoin)
    + [strokeWeight()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#strokeweightn)
  * Curves
    + [bezier()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#bezierx1y1x2y2x3y3x4y4)
    + [Vertex](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#vertex)
    + [beginShape()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#beginshape)
    + [vertex()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#vertexx1x1)
    + [bezierVertex()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#beziervertexx2y2x3y3x4y4)
    + [endShape()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#endshapeclose)
- [Constants](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#constants)
    + [HALF_PI](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#half_pi)
    + [PI](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#pi)
    + [QUARTER_PI](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#quarter_pi)
    + [TAU](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#tau)
    + [TWO_PI](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#two_pi)
- [Structure](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#structure)
    + [setup()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#setup)
    + [draw()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#draw)
    + [push()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#push)
    + [pop()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#pop)
- [Environment](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#environment)
    + [width](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#width)
    + [height](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#height)
- [Rendering](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#rendering)
    + [createCanvas()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#createcanvas)
- [Transform](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#transform)
    + [rotate()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#rotateangle)
    + [translate()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#translatexy)
- [Data](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#data)
  * Array Functions
    + [append()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#appendarrayvalue)
    + [arrayCopy()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#arraycopysrcsrcpositiondstdstpositionlength)
    + [concat()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#concatab)
    + [reverse()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#reverselist)
    + [shorten()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#shortenlist)
    + [shuffle()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#shufflearraybool)
    + [sort()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#sortlistcount)
    + [splice()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#splicelistvalueposition)
    + [subset()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#subsetliststartcount)
  * Loading & Displaying
    + [image()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#imagenamexy)
    + [symbol()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#symbolnamexy)
    + [get()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#geturl)
  * Calculation
    + [abs()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#absn)
    + [ceil()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#ceiln)
    + [constrain()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#constrainnlowhigh)
    + [dist()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#distx1y1x2y2)
    + [exp()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#expn)
    + [floor()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#floorn)
    + [lerp()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#lerpstartstopamt)
    + [logarithm()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#logarithmn)
    + [mag()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#magab)
    + [map()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#mapvaluestart1stop1start2stop2)
    + [max()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#maxn0)
    + [min()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#minn0)
    + [norm()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#normvaluestartstop)
    + [pow()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#powne)
    + [round()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#roundn)
    + [sq()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#sqn)
    + [sqrt()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#sqrtn)
  * Noise
    + [noise()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#noisexyz)
  * Random
    + [random()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#randomminmax)
  * Trigonometry
    + [cos()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#cosangle)
    + [sin()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#sinangle)
    + [tan()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#tanangle)
    + [degrees()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#degreesangle)
    + [radians()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#radiansangle)
- [Typography](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#typography)
  * Attributes
    + [textAlign()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#textalignhorizalign)
    + [textLeading()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#textleadingn)
    + [textSize()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#textsizesize)
  * Loading & Displaying
    + [text()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#textstrxyx2y2)
    + [textFont()](https://github.com/jacopocolo/p5-sketchplugin/blob/master/reference.md#textfontfontname)

## Debugging your code
Debugging in Sketch is not simple. Sketch doesn’t have a console but relies on macOS Console for debugging. If your code is not working as expected, you can take a look there and see if something pops up. If you are running macOS Sierra or above, see [this thread](http://sketchplugins.com/d/50-i-can-t-get-anymore-debugging-from-console-app-system-log) to configure your Console to catch Sketch issues.

Even before looking at the console, however, do this:
- check if the functions you are using are documented in the reference page and if you are using the right parameters.
- remember that in p5.js the draw function is called continuously while in p5.sketchplugin it's called only once. Consider if you need to wrap your function in a loop to achieve what you want.

## To-do list

### 1.1.0

- Make sure that the code is stored in [preferences](http://mail.sketchplugins.com/pipermail/dev_sketchplugins.com/2015-February/003019.html) so updates don’t wipe users work

- Fix typo

- Include autocomplete in editor

- get() broke with Sketch 47. Figure out how to support it again

### Future releases

- Implement 'rgb(255,255,255)' and 'rgba(255,255,255,1)' notations for fill and stroke

- Set a default for create canvas so it create a canvas even if the function is not actually called

- Implement border radius for rect()

- Implement blendMode()

- Allow users to create multiple canvas artboards? Maybe?

- Figure out if it's possible to import external libraries?


================================================
FILE: appcast.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"  xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>P5Sketch</title>
    <link>http://sparkle-project.org/files/sparkletestcast.xml</link>
    <description>A plugin for running p5.js code inside Sketch</description>
    <language>en</language>
      <item>
        <title>Version 0.9.2</title>
        <description>
          <![CDATA[
            <ul>
              <li>Pre-release version</li>
            </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/v0.9.1/p5.0.9.1.sketchplugin.zip" sparkle:version="0.9.2" />
      </item>
      <item>
        <title>Version 0.9.3</title>
        <description>
          <![CDATA[
            <ul>
              <li>Test update</li>
            </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/v0.9.3/p5.0.9.3.sketchplugin.zip" sparkle:version="0.9.3" />
      </item>
      <item>
        <title>Version 0.9.4</title>
        <description>
          <![CDATA[
            <ul>
              <li>Fixes bug #2</li>
              <li>Editor window is now resizeable</li>
              <li>Editor has a new visual style that should make things a bit easier</li>
            </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/v0.9.4/p5.0.9.4.sketchplugin.zip" sparkle:version="0.9.4" />
      </item>
      <item>
        <title>Version 0.9.5</title>
        <description>
          <![CDATA[
            <ul>
              <li>Updated the UI</li>
              <li>Added a generative grid and simple bar chart to presets</li>
              <li>Fixed a bug with translate in bezierVertex</li>
              <li>Removed the get() function because it crashed in Sketch 47</li>
            </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/v0.9.5/p5.0.9.5.sketchplugin.zip" sparkle:version="0.9.5" />
      </item>
      <item>
        <title>Version 1.0.0</title>
        <description>
          <![CDATA[
            <ul>
              <li>Minor bug fixes and official 1.0 release</li>
            </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/v1.0/p5.1.0.0.sketchplugin.zip" sparkle:version="1.0.0" />
      </item>
      <item>
        <title>Version 1.1.0</title>
        <description>
          <![CDATA[
          <ul>
            <li>Ready for Sketch 48</li>
            <li>Added get() function for fetching remote JSON and get() code example</li>
            <li>Added release notes in the UI 👋🏻</li>
            <li>Stored code between updates in preferences</li>
            <li>Fixed a shameful typo in the UI 🤦🏻</li>
          </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/V1.1.0/p5.1.1.0.sketchplugin.zip" sparkle:version="1.1.0" />
      </item>
      <item>
        <title>Version 1.1.1</title>
        <description>
          <![CDATA[
          <ul>
            <li>Added a warning message in case the code has errors</li>
            <li>(Hopefully) fixed a terrible case of: https://xkcd.com/1171/</li>
          </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/V1.1.1/p5.1.1.1.sketchplugin.zip" sparkle:version="1.1.1" />
      </item>
      <item>
        <title>Version 1.1.2</title>
        <description>
          <![CDATA[
          <ul>
            <li>Added support for Sketch 50</li>
          </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/V1.1.2/p5.1.1.2.sketchplugin.zip" sparkle:version="1.1.2" />
      </item>
      <item>
        <title>Version 1.1.3</title>
        <description>
          <![CDATA[
          <ul>
            <li>Added support for Sketch 52</li>
          </ul>
          ]]>
        </description>
        <enclosure url="https://github.com/jacopocolo/p5-sketchplugin/releases/download/V1.1.3/p5.1.1.3.sketchplugin.zip" sparkle:version="1.1.3" />
      </item>
  </channel>
</rss>


================================================
FILE: full-reference.md
================================================
# Full API Reference



## Color

### Creating & Reading

~alpha()~: not implemented

~blue()~: not implemented

~brightness()~: not implemented

~color()~: not implemented

~green()~: not implemented

~hue()~: not implemented

~lerpColor()~: not implemented

~lightness()~: not implemented

~red()~: not implemented

~saturation()~: not implemented



### Setting

background()

~clear()~: doesn't make sense in Sketch

~colorMode()~

fill()

noFill()

noStroke()

stroke()



## Shape

### 2D Primitives

arc()

ellipse()

line()

point()

quad()

rect()

triangle()



### Attributes

~ellipseMode()~: not implemented

~noSmooth()~: doesn't make sense in Sketch

~rectMode()~: not implemented

~smooth()~: doesn't make sense in Sketch

strokeCap()

strokeJoin()

strokeWeight()



### Curves

bezier()

~bezierPoint()~: not implemented

~bezierTangent()~: not implemented

~curve()~: not implemented

~curveTightness()~: not implemented

~curvePoint()~: not implemented

~curveTangent()~: not implemented



### Vertex

~beginContour()~: not implemented

beginShape()

bezierVertex()

~curveVertex()~: not implemented

~endContour()~: not implemented

endShape()

~quadraticVertex()~: not implemented

vertex()



### ~3D Models~

~loadModel()~: not implemented

~model()~: not implemented



### ~3D Primitives~

~plane()~: not implemented

~box()~: not implemented

~sphere()~: not implemented

~cylinder()~: not implemented

~cone()~: not implemented

~ellipsoid()~: not implemented

~torus()~: not implemented



## Constants

HALF_PI

PI

QUARTER_PI

TAU

TWO_PI



## Structure

~preload()~: doesn't make sense in Sketch

setup()

draw()

~remove()~: doesn't make sense in Sketch

~noLoop()~: doesn't make sense in Sketch

~loop()~: doesn't make sense in Sketch

push()

pop()

~redraw()~: doesn't make sense in Sketch



## Environment

~print()~: doesn't make sense in Sketch

~frameCount~: doesn't make sense in Sketch

~focused~: doesn't make sense in Sketch

~cursor()~: doesn't make sense in Sketch

~frameRate()~: doesn't make sense in Sketch

~noCursor()~: doesn't make sense in Sketch

~displayWidth~: doesn't make sense in Sketch

~displayHeight~: doesn't make sense in Sketch

~windowWidth~: doesn't make sense in Sketch

~windowHeight~: doesn't make sense in Sketch

~windowResized()~: doesn't make sense in Sketch

width

height

~fullscreen()~: doesn't make sense in Sketch

~pixelDensity()~: doesn't make sense in Sketch

~displayDensity()~: doesn't make sense in Sketch

~getURL()~: not implemented

~getURLPath()~: not implemented

~getURLParams()~: not implemented



## Rendering

createCanvas()

~resizeCanvas()~: not implemented

~noCanvas()~: not implemented

~createGraphics()~: not implemented

~blendMode()~: not implemented



## Transform

~applyMatrix()~: not implemented

~resetMatrix()~: not implemented

rotate()

~rotateX()~: not implemented

~rotateY()~: not implemented

~rotateZ()~: not implemented

~scale()~: not implemented

~shearX()~: not implemented

~shearY()~: not implemented

translate()



## Data

### ~Dictionary~

~p5.TypedDict~: not implemented

~p5.NumberDict~: not implemented



### Array Functions

append()

arrayCopy()

concat()

reverse()

shorten()

shuffle()

sort()

splice()

subset()



### Conversion

float()

int()

str()

boolean()

byte()

char()

unchar()

hex()

unhex()



### String Functions

join()

match()

matchAll()

nf()

nfc()

nfp()

nfs()

split()

splitTokens()

trim()



## Image

~createImage()~: not implemented

~saveCanvas()~: not implemented

~saveFrames()~: not implemented

~p5.Image~: not implemented



### Loading & Displaying

~loadImage()~: doesn't make sense in Sketch

image()

~tint()~: not implemented

~noTint()~: not implemented

~imageMode()~: not implemented



### ~Pixels~

~pixels~: not implemented

~blend()~: not implemented

~copy()~: not implemented

~filter()~: not implemented

~get()~: not implemented

~loadPixels()~: not implemented

~set()~: not implemented

~updatePixels()~: not implemented



## Math

~createVector()~: not implemented

~p5.Vector~: not implemented



### Calculation

abs()

ceil()

constrain()

dist()

exp()

floor()

lerp()

log()

mag()

map()

max()

min()

norm()

pow()

round()

sq()

sqrt()



### Noise

noise()

noiseDetail()

noiseSeed()

Random

randomSeed()

random()

randomGaussian()



### Trigonometry

acos()

asin()

atan()

atan2()

cos()

sin()

tan()

degrees()

radians()

~angleMode()~: not implemented



## Typography

### Attributes

textAlign()

textLeading()

textSize()

~textStyle()~: not implemented

~textWidth()~: not implemented

~textAscent()~: not implemented

~textDescent()~: not implemented



### Loading & Displaying

~loadFont()~: doesn't make sense in Sketch

text()

textFont()



### Font

~p5.Font~: not implemented



## ~Lights, Camera~

### ~Camera~

~camera()~: not implemented

~perspective()~: not implemented

~ortho()~: not implemented



### ~Lights~

~ambientLight()~: not implemented

~directionalLight()~: not implemented

~pointLight()~: not implemented



### ~Material~

~normalMaterial()~: not implemented

~texture()~: not implemented

~ambientMaterial()~: not implemented

~specularMaterial()~: not implemented



~p5.RendererGL~: not implemented


================================================
FILE: p5.sketchplugin/Contents/Resources/codemirror.css
================================================
.CodeMirror {
    font-family: monospace;
    height: 300px;
    color: #000
}

.CodeMirror-lines {
    padding: 4px 0
}

.CodeMirror pre {
    padding: 0 4px
}

.CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler {
    background-color: #fff
}

.CodeMirror-gutters {
    /*border-right: 1px solid #ddd;*/
    background-color: #f7f7f7;
    white-space: nowrap
}

.CodeMirror-linenumber {
    padding: 0 3px 0 5px;
    min-width: 20px;
    text-align: right;
    color: #999;
    white-space: nowrap
}

.CodeMirror-guttermarker {
    color: #000
}

.CodeMirror-guttermarker-subtle {
    color: #999
}

.CodeMirror-cursor {
    border-left: 1px solid #000;
    border-right: none;
    width: 0
}

.CodeMirror div.CodeMirror-secondarycursor {
    border-left: 1px solid silver
}

.cm-fat-cursor .CodeMirror-cursor {
    width: auto;
    border: 0;
    background: #7e7
}

.cm-fat-cursor div.CodeMirror-cursors {
    z-index: 1
}

.cm-animate-fat-cursor {
    width: auto;
    border: 0;
    -webkit-animation: blink 1.06s steps(1) infinite;
    animation: blink 1.06s steps(1) infinite;
    background-color: #7e7
}

@-webkit-keyframes blink {
    50% {
        background-color: transparent
    }
}

@keyframes blink {
    50% {
        background-color: transparent
    }
}

.cm-tab {
    display: inline-block;
    text-decoration: inherit
}

.CodeMirror-ruler {
    border-left: 1px solid #ccc;
    position: absolute
}

.cm-s-default .cm-header {
    color: blue
}

.cm-s-default .cm-quote {
    color: #090
}

.cm-negative {
    color: #d44
}

.cm-positive {
    color: #292
}

.cm-header, .cm-strong {
    font-weight: 700
}

.cm-em {
    font-style: italic
}

.cm-link {
    text-decoration: underline
}

.cm-strikethrough {
    text-decoration: line-through
}

.cm-s-default .cm-keyword {
    color: #708
}

.cm-s-default .cm-atom {
    color: #219
}

.cm-s-default .cm-number {
    color: #164
}

.cm-s-default .cm-def {
    color: #00f
}

.cm-s-default .cm-variable-2 {
    color: #05a
}

.cm-s-default .cm-variable-3 {
    color: #085
}

.cm-s-default .cm-comment {
    color: #a50
}

.cm-s-default .cm-string {
    color: #a11
}

.cm-s-default .cm-string-2 {
    color: #f50
}

.cm-s-default .cm-meta, .cm-s-default .cm-qualifier {
    color: #555
}

.cm-s-default .cm-builtin {
    color: #30a
}

.cm-s-default .cm-bracket {
    color: #997
}

.cm-s-default .cm-tag {
    color: #170
}

.cm-s-default .cm-attribute {
    color: #00c
}

.cm-s-default .cm-hr {
    color: #999
}

.cm-s-default .cm-link {
    color: #00c
}

.cm-invalidchar, .cm-s-default .cm-error {
    color: red
}

.CodeMirror-composing {
    border-bottom: 2px solid
}

div.CodeMirror span.CodeMirror-matchingbracket {
    color: #0f0
}

div.CodeMirror span.CodeMirror-nonmatchingbracket {
    color: #f22
}

.CodeMirror-matchingtag {
    background: rgba(255, 150, 0, .3)
}

.CodeMirror-activeline-background {
    background: #e8f2ff
}

.CodeMirror {
    position: relative;
    overflow: hidden;
    background: #fff
}

.CodeMirror-scroll {
    overflow: scroll!important;
    margin-bottom: -30px;
    margin-right: -30px;
    padding-bottom: 30px;
    height: 100%;
    outline: none;
    position: relative
}

.CodeMirror-sizer {
    position: relative;
    border-right: 30px solid transparent
}

.CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar {
    position: absolute;
    z-index: 6;
    display: none
}

.CodeMirror-vscrollbar {
    right: 0;
    top: 0;
    overflow-x: hidden;
    overflow-y: scroll
}

.CodeMirror-hscrollbar {
    bottom: 0;
    left: 0;
    overflow-y: hidden;
    overflow-x: scroll
}

.CodeMirror-scrollbar-filler {
    right: 0;
    bottom: 0
}

.CodeMirror-gutter-filler {
    left: 0;
    bottom: 0
}

.CodeMirror-gutters {
    position: absolute;
    left: 0;
    top: 0;
    min-height: 100%;
    z-index: 3
}

.CodeMirror-gutter {
    white-space: normal;
    height: 100%;
    display: inline-block;
    vertical-align: top;
    margin-bottom: -30px;
    *zoom: 1;
    *display: inline
}

.CodeMirror-gutter-wrapper {
    position: absolute;
    z-index: 4;
    background: none!important;
    border: none!important
}

.CodeMirror-gutter-background {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 4
}

.CodeMirror-gutter-elt {
    position: absolute;
    cursor: default;
    z-index: 4
}

.CodeMirror-gutter-wrapper {
    -webkit-user-select: none;
    -moz-user-select: none;
    user-select: none
}

.CodeMirror-lines {
    cursor: text;
    min-height: 1px
}

.CodeMirror pre {
    border-radius: 0;
    border-width: 0;
    background: transparent;
    font-family: inherit;
    font-size: inherit;
    margin: 0;
    white-space: pre;
    word-wrap: normal;
    line-height: inherit;
    color: inherit;
    z-index: 2;
    position: relative;
    overflow: visible;
    -webkit-tap-highlight-color: transparent;
    -webkit-font-variant-ligatures: none;
    font-variant-ligatures: none
}

.CodeMirror-wrap pre {
    word-wrap: break-word;
    white-space: pre-wrap;
    word-break: normal
}

.CodeMirror-linebackground {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    z-index: 0
}

.CodeMirror-linewidget {
    position: relative;
    z-index: 2;
    overflow: auto
}

.CodeMirror-code {
    outline: none
}

.CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber, .CodeMirror-scroll, .CodeMirror-sizer {
    box-sizing: content-box
}

.CodeMirror-measure {
    position: absolute;
    width: 100%;
    height: 0;
    overflow: hidden;
    visibility: hidden
}

.CodeMirror-cursor {
    position: absolute
}

.CodeMirror-measure pre {
    position: static
}

div.CodeMirror-cursors {
    visibility: hidden;
    position: relative;
    z-index: 3
}

.CodeMirror-focused div.CodeMirror-cursors, div.CodeMirror-dragcursors {
    visibility: visible
}

.CodeMirror-selected {
    background: #d9d9d9
}

.CodeMirror-focused .CodeMirror-selected {
    background: #d7d4f0
}

.CodeMirror-crosshair {
    cursor: crosshair
}

.CodeMirror-line::selection, .CodeMirror-line>span::selection, .CodeMirror-line>span>span::selection {
    background: #d7d4f0
}

.CodeMirror-line::-moz-selection, .CodeMirror-line>span::-moz-selection, .CodeMirror-line>span>span::-moz-selection {
    background: #d7d4f0
}

.CodeMirror-selected {
  background:#d7d4f0 !important;
}

.cm-searching {
    background: #ffa;
    background: rgba(255, 255, 0, .4)
}

.CodeMirror span {
    *vertical-align: text-bottom
}

.cm-force-border {
    padding-right: .1px
}

@media print {
    .CodeMirror div.CodeMirror-cursors {
        visibility: hidden
    }
}

.cm-tab-wrap-hack:after {
    content: ""
}

span.CodeMirror-selectedtext {
    background: none
}

.CodeMirror-lint-markers {
    width: 16px
}

.CodeMirror-lint-tooltip {
    background-color: infobackground;
    border: 1px solid #000;
    border-radius: 4px 4px 4px 4px;
    color: infotext;
    font-family: monospace;
    font-size: 10pt;
    overflow: hidden;
    padding: 2px 5px;
    position: fixed;
    white-space: pre;
    white-space: pre-wrap;
    z-index: 100;
    max-width: 600px;
    opacity: 0;
    transition: opacity .4s;
    -moz-transition: opacity .4s;
    -webkit-transition: opacity .4s;
    -o-transition: opacity .4s;
    -ms-transition: opacity .4s
}

.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
    background-position: 0 100%;
    background-repeat: repeat-x
}

.CodeMirror-lint-mark-error {
    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
}

.CodeMirror-lint-mark-warning {
    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=")
}

.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
    background-position: 50%;
    background-repeat: no-repeat;
    cursor: pointer;
    display: inline-block;
    height: 16px;
    width: 16px;
    vertical-align: middle;
    position: relative
}

.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
    padding-left: 18px;
    background-position: 0 0;
    background-repeat: no-repeat
}

.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=")
}

.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=")
}

.CodeMirror-lint-marker-multiple {
    background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
    background-repeat: no-repeat;
    background-position: 100% 100%;
    width: 100%;
    height: 100%
}


================================================
FILE: p5.sketchplugin/Contents/Resources/codemirror.js
================================================
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// This is CodeMirror (http://codemirror.net), a code editor
// implemented in JavaScript on top of the browser's DOM.
//
// You can find some technical background for some of the code below
// at http://marijnhaverbeke.nl/blog/#cm-internals .

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    module.exports = mod();
  else if (typeof define == "function" && define.amd) // AMD
    return define([], mod);
  else // Plain browser env
    this.CodeMirror = mod();
})(function() {
  "use strict";

  // BROWSER SNIFFING

  // Kludges for bugs and behavior differences that can't be feature
  // detected are enabled based on userAgent etc sniffing.

  var gecko = /gecko\/\d/i.test(navigator.userAgent);
  var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
  var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);
  var ie = ie_upto10 || ie_11up;
  var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]);
  var webkit = /WebKit\//.test(navigator.userAgent);
  var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
  var chrome = /Chrome\//.test(navigator.userAgent);
  var presto = /Opera\//.test(navigator.userAgent);
  var safari = /Apple Computer/.test(navigator.vendor);
  var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
  var phantom = /PhantomJS/.test(navigator.userAgent);

  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
  // This is woefully incomplete. Suggestions for alternative methods welcome.
  var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
  var mac = ios || /Mac/.test(navigator.platform);
  var windows = /win/i.test(navigator.platform);

  var presto_version = presto && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
  if (presto_version) presto_version = Number(presto_version[1]);
  if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
  // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
  var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
  var captureRightClick = gecko || (ie && ie_version >= 9);

  // Optimize some code when these features are not used.
  var sawReadOnlySpans = false, sawCollapsedSpans = false;

  // EDITOR CONSTRUCTOR

  // A CodeMirror instance represents an editor. This is the object
  // that user code is usually dealing with.

  function CodeMirror(place, options) {
    if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);

    this.options = options = options ? copyObj(options) : {};
    // Determine effective options based on given values and defaults.
    copyObj(defaults, options, false);
    setGuttersForLineNumbers(options);

    var doc = options.value;
    if (typeof doc == "string") doc = new Doc(doc, options.mode);
    this.doc = doc;

    var input = new CodeMirror.inputStyles[options.inputStyle](this);
    var display = this.display = new Display(place, doc, input);
    display.wrapper.CodeMirror = this;
    updateGutters(this);
    themeChanged(this);
    if (options.lineWrapping)
      this.display.wrapper.className += " CodeMirror-wrap";
    if (options.autofocus && !mobile) display.input.focus();
    initScrollbars(this);

    this.state = {
      keyMaps: [],  // stores maps added by addKeyMap
      overlays: [], // highlighting overlays, as added by addOverlay
      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
      overwrite: false,
      delayingBlurEvent: false,
      focused: false,
      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll
      draggingText: false,
      highlight: new Delayed(), // stores highlight worker timeout
      keySeq: null,  // Unfinished key sequence
      specialChars: null
    };

    var cm = this;

    // Override magic textarea content restore that IE sometimes does
    // on our hidden textarea on reload
    if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20);

    registerEventHandlers(this);
    ensureGlobalHandlers();

    startOperation(this);
    this.curOp.forceUpdate = true;
    attachDoc(this, doc);

    if ((options.autofocus && !mobile) || cm.hasFocus())
      setTimeout(bind(onFocus, this), 20);
    else
      onBlur(this);

    for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
      optionHandlers[opt](this, options[opt], Init);
    maybeUpdateLineNumberWidth(this);
    if (options.finishInit) options.finishInit(this);
    for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
    endOperation(this);
    // Suppress optimizelegibility in Webkit, since it breaks text
    // measuring on line wrapping boundaries.
    if (webkit && options.lineWrapping &&
        getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
      display.lineDiv.style.textRendering = "auto";
  }

  // DISPLAY CONSTRUCTOR

  // The display handles the DOM integration, both for input reading
  // and content drawing. It holds references to DOM nodes and
  // display-related state.

  function Display(place, doc, input) {
    var d = this;
    this.input = input;

    // Covers bottom-right square when both scrollbars are present.
    d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
    d.scrollbarFiller.setAttribute("cm-not-content", "true");
    // Covers bottom of gutter when coverGutterNextToScrollbar is on
    // and h scrollbar is present.
    d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
    d.gutterFiller.setAttribute("cm-not-content", "true");
    // Will contain the actual code, positioned to cover the viewport.
    d.lineDiv = elt("div", null, "CodeMirror-code");
    // Elements are added to these to represent selection and cursors.
    d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
    d.cursorDiv = elt("div", null, "CodeMirror-cursors");
    // A visibility: hidden element used to find the size of things.
    d.measure = elt("div", null, "CodeMirror-measure");
    // When lines outside of the viewport are measured, they are drawn in this.
    d.lineMeasure = elt("div", null, "CodeMirror-measure");
    // Wraps everything that needs to exist inside the vertically-padded coordinate system
    d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
                      null, "position: relative; outline: none");
    // Moved around its parent to cover visible view.
    d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
    // Set to the height of the document, allowing scrolling.
    d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
    d.sizerWidth = null;
    // Behavior of elts with overflow: auto and padding is
    // inconsistent across browsers. This is used to ensure the
    // scrollable area is big enough.
    d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
    // Will contain the gutters, if any.
    d.gutters = elt("div", null, "CodeMirror-gutters");
    d.lineGutter = null;
    // Actual scrollable element.
    d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
    d.scroller.setAttribute("tabIndex", "-1");
    // The element in which the editor lives.
    d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");

    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
    if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
    if (!webkit && !(gecko && mobile)) d.scroller.draggable = true;

    if (place) {
      if (place.appendChild) place.appendChild(d.wrapper);
      else place(d.wrapper);
    }

    // Current rendered range (may be bigger than the view window).
    d.viewFrom = d.viewTo = doc.first;
    d.reportedViewFrom = d.reportedViewTo = doc.first;
    // Information about the rendered lines.
    d.view = [];
    d.renderedView = null;
    // Holds info about a single rendered line when it was rendered
    // for measurement, while not in view.
    d.externalMeasured = null;
    // Empty space (in pixels) above the view
    d.viewOffset = 0;
    d.lastWrapHeight = d.lastWrapWidth = 0;
    d.updateLineNumbers = null;

    d.nativeBarWidth = d.barHeight = d.barWidth = 0;
    d.scrollbarsClipped = false;

    // Used to only resize the line number gutter when necessary (when
    // the amount of lines crosses a boundary that makes its width change)
    d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
    // Set to true when a non-horizontal-scrolling line widget is
    // added. As an optimization, line widget aligning is skipped when
    // this is false.
    d.alignWidgets = false;

    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;

    // Tracks the maximum line length so that the horizontal scrollbar
    // can be kept static when scrolling.
    d.maxLine = null;
    d.maxLineLength = 0;
    d.maxLineChanged = false;

    // Used for measuring wheel scrolling granularity
    d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;

    // True when shift is held down.
    d.shift = false;

    // Used to track whether anything happened since the context menu
    // was opened.
    d.selForContextMenu = null;

    d.activeTouch = null;

    input.init(d);
  }

  // STATE UPDATES

  // Used to get the editor into a consistent state again when options change.

  function loadMode(cm) {
    cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
    resetModeState(cm);
  }

  function resetModeState(cm) {
    cm.doc.iter(function(line) {
      if (line.stateAfter) line.stateAfter = null;
      if (line.styles) line.styles = null;
    });
    cm.doc.frontier = cm.doc.first;
    startWorker(cm, 100);
    cm.state.modeGen++;
    if (cm.curOp) regChange(cm);
  }

  function wrappingChanged(cm) {
    if (cm.options.lineWrapping) {
      addClass(cm.display.wrapper, "CodeMirror-wrap");
      cm.display.sizer.style.minWidth = "";
      cm.display.sizerWidth = null;
    } else {
      rmClass(cm.display.wrapper, "CodeMirror-wrap");
      findMaxLine(cm);
    }
    estimateLineHeights(cm);
    regChange(cm);
    clearCaches(cm);
    setTimeout(function(){updateScrollbars(cm);}, 100);
  }

  // Returns a function that estimates the height of a line, to use as
  // first approximation until the line becomes visible (and is thus
  // properly measurable).
  function estimateHeight(cm) {
    var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
    var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
    return function(line) {
      if (lineIsHidden(cm.doc, line)) return 0;

      var widgetsHeight = 0;
      if (line.widgets) for (var i = 0; i < line.widgets.length; i++) {
        if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
      }

      if (wrapping)
        return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
      else
        return widgetsHeight + th;
    };
  }

  function estimateLineHeights(cm) {
    var doc = cm.doc, est = estimateHeight(cm);
    doc.iter(function(line) {
      var estHeight = est(line);
      if (estHeight != line.height) updateLineHeight(line, estHeight);
    });
  }

  function themeChanged(cm) {
    cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
      cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
    clearCaches(cm);
  }

  function guttersChanged(cm) {
    updateGutters(cm);
    regChange(cm);
    setTimeout(function(){alignHorizontally(cm);}, 20);
  }

  // Rebuild the gutter elements, ensure the margin to the left of the
  // code matches their width.
  function updateGutters(cm) {
    var gutters = cm.display.gutters, specs = cm.options.gutters;
    removeChildren(gutters);
    for (var i = 0; i < specs.length; ++i) {
      var gutterClass = specs[i];
      var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
      if (gutterClass == "CodeMirror-linenumbers") {
        cm.display.lineGutter = gElt;
        gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
      }
    }
    gutters.style.display = i ? "" : "none";
    updateGutterSpace(cm);
  }

  function updateGutterSpace(cm) {
    var width = cm.display.gutters.offsetWidth;
    cm.display.sizer.style.marginLeft = width + "px";
  }

  // Compute the character length of a line, taking into account
  // collapsed ranges (see markText) that might hide parts, and join
  // other lines onto it.
  function lineLength(line) {
    if (line.height == 0) return 0;
    var len = line.text.length, merged, cur = line;
    while (merged = collapsedSpanAtStart(cur)) {
      var found = merged.find(0, true);
      cur = found.from.line;
      len += found.from.ch - found.to.ch;
    }
    cur = line;
    while (merged = collapsedSpanAtEnd(cur)) {
      var found = merged.find(0, true);
      len -= cur.text.length - found.from.ch;
      cur = found.to.line;
      len += cur.text.length - found.to.ch;
    }
    return len;
  }

  // Find the longest line in the document.
  function findMaxLine(cm) {
    var d = cm.display, doc = cm.doc;
    d.maxLine = getLine(doc, doc.first);
    d.maxLineLength = lineLength(d.maxLine);
    d.maxLineChanged = true;
    doc.iter(function(line) {
      var len = lineLength(line);
      if (len > d.maxLineLength) {
        d.maxLineLength = len;
        d.maxLine = line;
      }
    });
  }

  // Make sure the gutters options contains the element
  // "CodeMirror-linenumbers" when the lineNumbers option is true.
  function setGuttersForLineNumbers(options) {
    var found = indexOf(options.gutters, "CodeMirror-linenumbers");
    if (found == -1 && options.lineNumbers) {
      options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]);
    } else if (found > -1 && !options.lineNumbers) {
      options.gutters = options.gutters.slice(0);
      options.gutters.splice(found, 1);
    }
  }

  // SCROLLBARS

  // Prepare DOM reads needed to update the scrollbars. Done in one
  // shot to minimize update/measure roundtrips.
  function measureForScrollbars(cm) {
    var d = cm.display, gutterW = d.gutters.offsetWidth;
    var docH = Math.round(cm.doc.height + paddingVert(cm.display));
    return {
      clientHeight: d.scroller.clientHeight,
      viewHeight: d.wrapper.clientHeight,
      scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
      viewWidth: d.wrapper.clientWidth,
      barLeft: cm.options.fixedGutter ? gutterW : 0,
      docHeight: docH,
      scrollHeight: docH + scrollGap(cm) + d.barHeight,
      nativeBarWidth: d.nativeBarWidth,
      gutterWidth: gutterW
    };
  }

  function NativeScrollbars(place, scroll, cm) {
    this.cm = cm;
    var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
    var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
    place(vert); place(horiz);

    on(vert, "scroll", function() {
      if (vert.clientHeight) scroll(vert.scrollTop, "vertical");
    });
    on(horiz, "scroll", function() {
      if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal");
    });

    this.checkedOverlay = false;
    // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
    if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px";
  }

  NativeScrollbars.prototype = copyObj({
    update: function(measure) {
      var needsH = measure.scrollWidth > measure.clientWidth + 1;
      var needsV = measure.scrollHeight > measure.clientHeight + 1;
      var sWidth = measure.nativeBarWidth;

      if (needsV) {
        this.vert.style.display = "block";
        this.vert.style.bottom = needsH ? sWidth + "px" : "0";
        var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
        // A bug in IE8 can cause this value to be negative, so guard it.
        this.vert.firstChild.style.height =
          Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
      } else {
        this.vert.style.display = "";
        this.vert.firstChild.style.height = "0";
      }

      if (needsH) {
        this.horiz.style.display = "block";
        this.horiz.style.right = needsV ? sWidth + "px" : "0";
        this.horiz.style.left = measure.barLeft + "px";
        var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
        this.horiz.firstChild.style.width =
          (measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
      } else {
        this.horiz.style.display = "";
        this.horiz.firstChild.style.width = "0";
      }

      if (!this.checkedOverlay && measure.clientHeight > 0) {
        if (sWidth == 0) this.overlayHack();
        this.checkedOverlay = true;
      }

      return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0};
    },
    setScrollLeft: function(pos) {
      if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos;
    },
    setScrollTop: function(pos) {
      if (this.vert.scrollTop != pos) this.vert.scrollTop = pos;
    },
    overlayHack: function() {
      var w = mac && !mac_geMountainLion ? "12px" : "18px";
      this.horiz.style.minHeight = this.vert.style.minWidth = w;
      var self = this;
      var barMouseDown = function(e) {
        if (e_target(e) != self.vert && e_target(e) != self.horiz)
          operation(self.cm, onMouseDown)(e);
      };
      on(this.vert, "mousedown", barMouseDown);
      on(this.horiz, "mousedown", barMouseDown);
    },
    clear: function() {
      var parent = this.horiz.parentNode;
      parent.removeChild(this.horiz);
      parent.removeChild(this.vert);
    }
  }, NativeScrollbars.prototype);

  function NullScrollbars() {}

  NullScrollbars.prototype = copyObj({
    update: function() { return {bottom: 0, right: 0}; },
    setScrollLeft: function() {},
    setScrollTop: function() {},
    clear: function() {}
  }, NullScrollbars.prototype);

  CodeMirror.scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};

  function initScrollbars(cm) {
    if (cm.display.scrollbars) {
      cm.display.scrollbars.clear();
      if (cm.display.scrollbars.addClass)
        rmClass(cm.display.wrapper, cm.display.scrollbars.addClass);
    }

    cm.display.scrollbars = new CodeMirror.scrollbarModel[cm.options.scrollbarStyle](function(node) {
      cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
      // Prevent clicks in the scrollbars from killing focus
      on(node, "mousedown", function() {
        if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0);
      });
      node.setAttribute("cm-not-content", "true");
    }, function(pos, axis) {
      if (axis == "horizontal") setScrollLeft(cm, pos);
      else setScrollTop(cm, pos);
    }, cm);
    if (cm.display.scrollbars.addClass)
      addClass(cm.display.wrapper, cm.display.scrollbars.addClass);
  }

  function updateScrollbars(cm, measure) {
    if (!measure) measure = measureForScrollbars(cm);
    var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
    updateScrollbarsInner(cm, measure);
    for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
      if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
        updateHeightsInViewport(cm);
      updateScrollbarsInner(cm, measureForScrollbars(cm));
      startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
    }
  }

  // Re-synchronize the fake scrollbars with the actual size of the
  // content.
  function updateScrollbarsInner(cm, measure) {
    var d = cm.display;
    var sizes = d.scrollbars.update(measure);

    d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
    d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";

    if (sizes.right && sizes.bottom) {
      d.scrollbarFiller.style.display = "block";
      d.scrollbarFiller.style.height = sizes.bottom + "px";
      d.scrollbarFiller.style.width = sizes.right + "px";
    } else d.scrollbarFiller.style.display = "";
    if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
      d.gutterFiller.style.display = "block";
      d.gutterFiller.style.height = sizes.bottom + "px";
      d.gutterFiller.style.width = measure.gutterWidth + "px";
    } else d.gutterFiller.style.display = "";
  }

  // Compute the lines that are visible in a given viewport (defaults
  // the the current scroll position). viewport may contain top,
  // height, and ensure (see op.scrollToPos) properties.
  function visibleLines(display, doc, viewport) {
    var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
    top = Math.floor(top - paddingTop(display));
    var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;

    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
    // forces those lines into the viewport (if possible).
    if (viewport && viewport.ensure) {
      var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
      if (ensureFrom < from) {
        from = ensureFrom;
        to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
      } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
        from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
        to = ensureTo;
      }
    }
    return {from: from, to: Math.max(to, from + 1)};
  }

  // LINE NUMBERS

  // Re-align line numbers and gutter marks to compensate for
  // horizontal scrolling.
  function alignHorizontally(cm) {
    var display = cm.display, view = display.view;
    if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
    var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
    var gutterW = display.gutters.offsetWidth, left = comp + "px";
    for (var i = 0; i < view.length; i++) if (!view[i].hidden) {
      if (cm.options.fixedGutter && view[i].gutter)
        view[i].gutter.style.left = left;
      var align = view[i].alignable;
      if (align) for (var j = 0; j < align.length; j++)
        align[j].style.left = left;
    }
    if (cm.options.fixedGutter)
      display.gutters.style.left = (comp + gutterW) + "px";
  }

  // Used to ensure that the line number gutter is still the right
  // size for the current document size. Returns true when an update
  // is needed.
  function maybeUpdateLineNumberWidth(cm) {
    if (!cm.options.lineNumbers) return false;
    var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
    if (last.length != display.lineNumChars) {
      var test = display.measure.appendChild(elt("div", [elt("div", last)],
                                                 "CodeMirror-linenumber CodeMirror-gutter-elt"));
      var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
      display.lineGutter.style.width = "";
      display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
      display.lineNumWidth = display.lineNumInnerWidth + padding;
      display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
      display.lineGutter.style.width = display.lineNumWidth + "px";
      updateGutterSpace(cm);
      return true;
    }
    return false;
  }

  function lineNumberFor(options, i) {
    return String(options.lineNumberFormatter(i + options.firstLineNumber));
  }

  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
  // but using getBoundingClientRect to get a sub-pixel-accurate
  // result.
  function compensateForHScroll(display) {
    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
  }

  // DISPLAY DRAWING

  function DisplayUpdate(cm, viewport, force) {
    var display = cm.display;

    this.viewport = viewport;
    // Store some values that we'll need later (but don't want to force a relayout for)
    this.visible = visibleLines(display, cm.doc, viewport);
    this.editorIsHidden = !display.wrapper.offsetWidth;
    this.wrapperHeight = display.wrapper.clientHeight;
    this.wrapperWidth = display.wrapper.clientWidth;
    this.oldDisplayWidth = displayWidth(cm);
    this.force = force;
    this.dims = getDimensions(cm);
    this.events = [];
  }

  DisplayUpdate.prototype.signal = function(emitter, type) {
    if (hasHandler(emitter, type))
      this.events.push(arguments);
  };
  DisplayUpdate.prototype.finish = function() {
    for (var i = 0; i < this.events.length; i++)
      signal.apply(null, this.events[i]);
  };

  function maybeClipScrollbars(cm) {
    var display = cm.display;
    if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
      display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
      display.heightForcer.style.height = scrollGap(cm) + "px";
      display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
      display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
      display.scrollbarsClipped = true;
    }
  }

  // Does the actual updating of the line display. Bails out
  // (returning false) when there is nothing to be done and forced is
  // false.
  function updateDisplayIfNeeded(cm, update) {
    var display = cm.display, doc = cm.doc;

    if (update.editorIsHidden) {
      resetView(cm);
      return false;
    }

    // Bail out if the visible area is already rendered and nothing changed.
    if (!update.force &&
        update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
        display.renderedView == display.view && countDirtyView(cm) == 0)
      return false;

    if (maybeUpdateLineNumberWidth(cm)) {
      resetView(cm);
      update.dims = getDimensions(cm);
    }

    // Compute a suitable new viewport (from & to)
    var end = doc.first + doc.size;
    var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
    var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
    if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
    if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
    if (sawCollapsedSpans) {
      from = visualLineNo(cm.doc, from);
      to = visualLineEndNo(cm.doc, to);
    }

    var different = from != display.viewFrom || to != display.viewTo ||
      display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
    adjustView(cm, from, to);

    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
    // Position the mover div to align with the current scroll position
    cm.display.mover.style.top = display.viewOffset + "px";

    var toUpdate = countDirtyView(cm);
    if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
        (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
      return false;

    // For big changes, we hide the enclosing element during the
    // update, since that speeds up the operations on most browsers.
    var focused = activeElt();
    if (toUpdate > 4) display.lineDiv.style.display = "none";
    patchDisplay(cm, display.updateLineNumbers, update.dims);
    if (toUpdate > 4) display.lineDiv.style.display = "";
    display.renderedView = display.view;
    // There might have been a widget with a focused element that got
    // hidden or updated, if so re-focus it.
    if (focused && activeElt() != focused && focused.offsetHeight) focused.focus();

    // Prevent selection and cursors from interfering with the scroll
    // width and height.
    removeChildren(display.cursorDiv);
    removeChildren(display.selectionDiv);
    display.gutters.style.height = 0;

    if (different) {
      display.lastWrapHeight = update.wrapperHeight;
      display.lastWrapWidth = update.wrapperWidth;
      startWorker(cm, 400);
    }

    display.updateLineNumbers = null;

    return true;
  }

  function postUpdateDisplay(cm, update) {
    var viewport = update.viewport;
    for (var first = true;; first = false) {
      if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
        // Clip forced viewport to actual scrollable area.
        if (viewport && viewport.top != null)
          viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)};
        // Updated line heights might result in the drawn area not
        // actually covering the viewport. Keep looping until it does.
        update.visible = visibleLines(cm.display, cm.doc, viewport);
        if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
          break;
      }
      if (!updateDisplayIfNeeded(cm, update)) break;
      updateHeightsInViewport(cm);
      var barMeasure = measureForScrollbars(cm);
      updateSelection(cm);
      setDocumentHeight(cm, barMeasure);
      updateScrollbars(cm, barMeasure);
    }

    update.signal(cm, "update", cm);
    if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
      update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
      cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
    }
  }

  function updateDisplaySimple(cm, viewport) {
    var update = new DisplayUpdate(cm, viewport);
    if (updateDisplayIfNeeded(cm, update)) {
      updateHeightsInViewport(cm);
      postUpdateDisplay(cm, update);
      var barMeasure = measureForScrollbars(cm);
      updateSelection(cm);
      setDocumentHeight(cm, barMeasure);
      updateScrollbars(cm, barMeasure);
      update.finish();
    }
  }

  function setDocumentHeight(cm, measure) {
    cm.display.sizer.style.minHeight = measure.docHeight + "px";
    var total = measure.docHeight + cm.display.barHeight;
    cm.display.heightForcer.style.top = total + "px";
    cm.display.gutters.style.height = Math.max(total + scrollGap(cm), measure.clientHeight) + "px";
  }

  // Read the actual heights of the rendered lines, and update their
  // stored heights to match.
  function updateHeightsInViewport(cm) {
    var display = cm.display;
    var prevBottom = display.lineDiv.offsetTop;
    for (var i = 0; i < display.view.length; i++) {
      var cur = display.view[i], height;
      if (cur.hidden) continue;
      if (ie && ie_version < 8) {
        var bot = cur.node.offsetTop + cur.node.offsetHeight;
        height = bot - prevBottom;
        prevBottom = bot;
      } else {
        var box = cur.node.getBoundingClientRect();
        height = box.bottom - box.top;
      }
      var diff = cur.line.height - height;
      if (height < 2) height = textHeight(display);
      if (diff > .001 || diff < -.001) {
        updateLineHeight(cur.line, height);
        updateWidgetHeight(cur.line);
        if (cur.rest) for (var j = 0; j < cur.rest.length; j++)
          updateWidgetHeight(cur.rest[j]);
      }
    }
  }

  // Read and store the height of line widgets associated with the
  // given line.
  function updateWidgetHeight(line) {
    if (line.widgets) for (var i = 0; i < line.widgets.length; ++i)
      line.widgets[i].height = line.widgets[i].node.offsetHeight;
  }

  // Do a bulk-read of the DOM positions and sizes needed to draw the
  // view, so that we don't interleave reading and writing to the DOM.
  function getDimensions(cm) {
    var d = cm.display, left = {}, width = {};
    var gutterLeft = d.gutters.clientLeft;
    for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
      left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft;
      width[cm.options.gutters[i]] = n.clientWidth;
    }
    return {fixedPos: compensateForHScroll(d),
            gutterTotalWidth: d.gutters.offsetWidth,
            gutterLeft: left,
            gutterWidth: width,
            wrapperWidth: d.wrapper.clientWidth};
  }

  // Sync the actual display DOM structure with display.view, removing
  // nodes for lines that are no longer in view, and creating the ones
  // that are not there yet, and updating the ones that are out of
  // date.
  function patchDisplay(cm, updateNumbersFrom, dims) {
    var display = cm.display, lineNumbers = cm.options.lineNumbers;
    var container = display.lineDiv, cur = container.firstChild;

    function rm(node) {
      var next = node.nextSibling;
      // Works around a throw-scroll bug in OS X Webkit
      if (webkit && mac && cm.display.currentWheelTarget == node)
        node.style.display = "none";
      else
        node.parentNode.removeChild(node);
      return next;
    }

    var view = display.view, lineN = display.viewFrom;
    // Loop over the elements in the view, syncing cur (the DOM nodes
    // in display.lineDiv) with the view as we go.
    for (var i = 0; i < view.length; i++) {
      var lineView = view[i];
      if (lineView.hidden) {
      } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
        var node = buildLineElement(cm, lineView, lineN, dims);
        container.insertBefore(node, cur);
      } else { // Already drawn
        while (cur != lineView.node) cur = rm(cur);
        var updateNumber = lineNumbers && updateNumbersFrom != null &&
          updateNumbersFrom <= lineN && lineView.lineNumber;
        if (lineView.changes) {
          if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false;
          updateLineForChanges(cm, lineView, lineN, dims);
        }
        if (updateNumber) {
          removeChildren(lineView.lineNumber);
          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
        }
        cur = lineView.node.nextSibling;
      }
      lineN += lineView.size;
    }
    while (cur) cur = rm(cur);
  }

  // When an aspect of a line changes, a string is added to
  // lineView.changes. This updates the relevant part of the line's
  // DOM structure.
  function updateLineForChanges(cm, lineView, lineN, dims) {
    for (var j = 0; j < lineView.changes.length; j++) {
      var type = lineView.changes[j];
      if (type == "text") updateLineText(cm, lineView);
      else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims);
      else if (type == "class") updateLineClasses(lineView);
      else if (type == "widget") updateLineWidgets(cm, lineView, dims);
    }
    lineView.changes = null;
  }

  // Lines with gutter elements, widgets or a background class need to
  // be wrapped, and have the extra elements added to the wrapper div
  function ensureLineWrapped(lineView) {
    if (lineView.node == lineView.text) {
      lineView.node = elt("div", null, null, "position: relative");
      if (lineView.text.parentNode)
        lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
      lineView.node.appendChild(lineView.text);
      if (ie && ie_version < 8) lineView.node.style.zIndex = 2;
    }
    return lineView.node;
  }

  function updateLineBackground(lineView) {
    var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
    if (cls) cls += " CodeMirror-linebackground";
    if (lineView.background) {
      if (cls) lineView.background.className = cls;
      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
    } else if (cls) {
      var wrap = ensureLineWrapped(lineView);
      lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
    }
  }

  // Wrapper around buildLineContent which will reuse the structure
  // in display.externalMeasured when possible.
  function getLineContent(cm, lineView) {
    var ext = cm.display.externalMeasured;
    if (ext && ext.line == lineView.line) {
      cm.display.externalMeasured = null;
      lineView.measure = ext.measure;
      return ext.built;
    }
    return buildLineContent(cm, lineView);
  }

  // Redraw the line's text. Interacts with the background and text
  // classes because the mode may output tokens that influence these
  // classes.
  function updateLineText(cm, lineView) {
    var cls = lineView.text.className;
    var built = getLineContent(cm, lineView);
    if (lineView.text == lineView.node) lineView.node = built.pre;
    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
    lineView.text = built.pre;
    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
      lineView.bgClass = built.bgClass;
      lineView.textClass = built.textClass;
      updateLineClasses(lineView);
    } else if (cls) {
      lineView.text.className = cls;
    }
  }

  function updateLineClasses(lineView) {
    updateLineBackground(lineView);
    if (lineView.line.wrapClass)
      ensureLineWrapped(lineView).className = lineView.line.wrapClass;
    else if (lineView.node != lineView.text)
      lineView.node.className = "";
    var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
    lineView.text.className = textClass || "";
  }

  function updateLineGutter(cm, lineView, lineN, dims) {
    if (lineView.gutter) {
      lineView.node.removeChild(lineView.gutter);
      lineView.gutter = null;
    }
    var markers = lineView.line.gutterMarkers;
    if (cm.options.lineNumbers || markers) {
      var wrap = ensureLineWrapped(lineView);
      var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
                                             "px; width: " + dims.gutterTotalWidth + "px");
      cm.display.input.setUneditable(gutterWrap);
      wrap.insertBefore(gutterWrap, lineView.text);
      if (lineView.line.gutterClass)
        gutterWrap.className += " " + lineView.line.gutterClass;
      if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
        lineView.lineNumber = gutterWrap.appendChild(
          elt("div", lineNumberFor(cm.options, lineN),
              "CodeMirror-linenumber CodeMirror-gutter-elt",
              "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
              + cm.display.lineNumInnerWidth + "px"));
      if (markers) for (var k = 0; k < cm.options.gutters.length; ++k) {
        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
        if (found)
          gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
                                     dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
      }
    }
  }

  function updateLineWidgets(cm, lineView, dims) {
    if (lineView.alignable) lineView.alignable = null;
    for (var node = lineView.node.firstChild, next; node; node = next) {
      var next = node.nextSibling;
      if (node.className == "CodeMirror-linewidget")
        lineView.node.removeChild(node);
    }
    insertLineWidgets(cm, lineView, dims);
  }

  // Build a line's DOM representation from scratch
  function buildLineElement(cm, lineView, lineN, dims) {
    var built = getLineContent(cm, lineView);
    lineView.text = lineView.node = built.pre;
    if (built.bgClass) lineView.bgClass = built.bgClass;
    if (built.textClass) lineView.textClass = built.textClass;

    updateLineClasses(lineView);
    updateLineGutter(cm, lineView, lineN, dims);
    insertLineWidgets(cm, lineView, dims);
    return lineView.node;
  }

  // A lineView may contain multiple logical lines (when merged by
  // collapsed spans). The widgets for all of them need to be drawn.
  function insertLineWidgets(cm, lineView, dims) {
    insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
    if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
      insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false);
  }

  function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
    if (!line.widgets) return;
    var wrap = ensureLineWrapped(lineView);
    for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
      var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
      if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true");
      positionLineWidget(widget, node, lineView, dims);
      cm.display.input.setUneditable(node);
      if (allowAbove && widget.above)
        wrap.insertBefore(node, lineView.gutter || lineView.text);
      else
        wrap.appendChild(node);
      signalLater(widget, "redraw");
    }
  }

  function positionLineWidget(widget, node, lineView, dims) {
    if (widget.noHScroll) {
      (lineView.alignable || (lineView.alignable = [])).push(node);
      var width = dims.wrapperWidth;
      node.style.left = dims.fixedPos + "px";
      if (!widget.coverGutter) {
        width -= dims.gutterTotalWidth;
        node.style.paddingLeft = dims.gutterTotalWidth + "px";
      }
      node.style.width = width + "px";
    }
    if (widget.coverGutter) {
      node.style.zIndex = 5;
      node.style.position = "relative";
      if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
    }
  }

  // POSITION OBJECT

  // A Pos instance represents a position within the text.
  var Pos = CodeMirror.Pos = function(line, ch) {
    if (!(this instanceof Pos)) return new Pos(line, ch);
    this.line = line; this.ch = ch;
  };

  // Compare two positions, return 0 if they are the same, a negative
  // number when a is less, and a positive number otherwise.
  var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };

  function copyPos(x) {return Pos(x.line, x.ch);}
  function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
  function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }

  // INPUT HANDLING

  function ensureFocus(cm) {
    if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
  }

  function isReadOnly(cm) {
    return cm.options.readOnly || cm.doc.cantEdit;
  }

  // This will be set to an array of strings when copying, so that,
  // when pasting, we know what kind of selections the copied text
  // was made out of.
  var lastCopied = null;

  function applyTextInput(cm, inserted, deleted, sel, origin) {
    var doc = cm.doc;
    cm.display.shift = false;
    if (!sel) sel = doc.sel;

    var textLines = splitLines(inserted), multiPaste = null;
    // When pasing N lines into N selections, insert one line per selection
    if (cm.state.pasteIncoming && sel.ranges.length > 1) {
      if (lastCopied && lastCopied.join("\n") == inserted)
        multiPaste = sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
      else if (textLines.length == sel.ranges.length)
        multiPaste = map(textLines, function(l) { return [l]; });
    }

    // Normal behavior is to insert the new text into every selection
    for (var i = sel.ranges.length - 1; i >= 0; i--) {
      var range = sel.ranges[i];
      var from = range.from(), to = range.to();
      if (range.empty()) {
        if (deleted && deleted > 0) // Handle deletion
          from = Pos(from.line, from.ch - deleted);
        else if (cm.state.overwrite && !cm.state.pasteIncoming) // Handle overwrite
          to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
      }
      var updateInput = cm.curOp.updateInput;
      var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
                         origin: origin || (cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
      makeChange(cm.doc, changeEvent);
      signalLater(cm, "inputRead", cm, changeEvent);
    }
    if (inserted && !cm.state.pasteIncoming)
      triggerElectric(cm, inserted);

    ensureCursorVisible(cm);
    cm.curOp.updateInput = updateInput;
    cm.curOp.typing = true;
    cm.state.pasteIncoming = cm.state.cutIncoming = false;
  }

  function triggerElectric(cm, inserted) {
    // When an 'electric' character is inserted, immediately trigger a reindent
    if (!cm.options.electricChars || !cm.options.smartIndent) return;
    var sel = cm.doc.sel;

    for (var i = sel.ranges.length - 1; i >= 0; i--) {
      var range = sel.ranges[i];
      if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue;
      var mode = cm.getModeAt(range.head);
      var indented = false;
      if (mode.electricChars) {
        for (var j = 0; j < mode.electricChars.length; j++)
          if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
            indented = indentLine(cm, range.head.line, "smart");
            break;
          }
      } else if (mode.electricInput) {
        if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
          indented = indentLine(cm, range.head.line, "smart");
      }
      if (indented) signalLater(cm, "electricInput", cm, range.head.line);
    }
  }

  function copyableRanges(cm) {
    var text = [], ranges = [];
    for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
      var line = cm.doc.sel.ranges[i].head.line;
      var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
      ranges.push(lineRange);
      text.push(cm.getRange(lineRange.anchor, lineRange.head));
    }
    return {text: text, ranges: ranges};
  }

  function disableBrowserMagic(field) {
    field.setAttribute("autocorrect", "off");
    field.setAttribute("autocapitalize", "off");
    field.setAttribute("spellcheck", "false");
  }

  // TEXTAREA INPUT STYLE

  function TextareaInput(cm) {
    this.cm = cm;
    // See input.poll and input.reset
    this.prevInput = "";

    // Flag that indicates whether we expect input to appear real soon
    // now (after some event like 'keypress' or 'input') and are
    // polling intensively.
    this.pollingFast = false;
    // Self-resetting timeout for the poller
    this.polling = new Delayed();
    // Tracks when input.reset has punted to just putting a short
    // string into the textarea instead of the full selection.
    this.inaccurateSelection = false;
    // Used to work around IE issue with selection being forgotten when focus moves away from textarea
    this.hasSelection = false;
    this.composing = null;
  };

  function hiddenTextarea() {
    var te = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none");
    var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
    // The textarea is kept positioned near the cursor to prevent the
    // fact that it'll be scrolled into view on input from scrolling
    // our fake cursor out of view. On webkit, when wrap=off, paste is
    // very slow. So make the area wide instead.
    if (webkit) te.style.width = "1000px";
    else te.setAttribute("wrap", "off");
    // If border: 0; -- iOS fails to open keyboard (issue #1287)
    if (ios) te.style.border = "1px solid black";
    disableBrowserMagic(te);
    return div;
  }

  TextareaInput.prototype = copyObj({
    init: function(display) {
      var input = this, cm = this.cm;

      // Wraps and hides input textarea
      var div = this.wrapper = hiddenTextarea();
      // The semihidden textarea that is focused when the editor is
      // focused, and receives input.
      var te = this.textarea = div.firstChild;
      display.wrapper.insertBefore(div, display.wrapper.firstChild);

      // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
      if (ios) te.style.width = "0px";

      on(te, "input", function() {
        if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null;
        input.poll();
      });

      on(te, "paste", function() {
        // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
        // Add a char to the end of textarea before paste occur so that
        // selection doesn't span to the end of textarea.
        if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
          var start = te.selectionStart, end = te.selectionEnd;
          te.value += "$";
          // The selection end needs to be set before the start, otherwise there
          // can be an intermediate non-empty selection between the two, which
          // can override the middle-click paste buffer on linux and cause the
          // wrong thing to get pasted.
          te.selectionEnd = end;
          te.selectionStart = start;
          cm.state.fakedLastChar = true;
        }
        cm.state.pasteIncoming = true;
        input.fastPoll();
      });

      function prepareCopyCut(e) {
        if (cm.somethingSelected()) {
          lastCopied = cm.getSelections();
          if (input.inaccurateSelection) {
            input.prevInput = "";
            input.inaccurateSelection = false;
            te.value = lastCopied.join("\n");
            selectInput(te);
          }
        } else if (!cm.options.lineWiseCopyCut) {
          return;
        } else {
          var ranges = copyableRanges(cm);
          lastCopied = ranges.text;
          if (e.type == "cut") {
            cm.setSelections(ranges.ranges, null, sel_dontScroll);
          } else {
            input.prevInput = "";
            te.value = ranges.text.join("\n");
            selectInput(te);
          }
        }
        if (e.type == "cut") cm.state.cutIncoming = true;
      }
      on(te, "cut", prepareCopyCut);
      on(te, "copy", prepareCopyCut);

      on(display.scroller, "paste", function(e) {
        if (eventInWidget(display, e)) return;
        cm.state.pasteIncoming = true;
        input.focus();
      });

      // Prevent normal selection in the editor (we handle our own)
      on(display.lineSpace, "selectstart", function(e) {
        if (!eventInWidget(display, e)) e_preventDefault(e);
      });

      on(te, "compositionstart", function() {
        var start = cm.getCursor("from");
        input.composing = {
          start: start,
          range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
        };
      });
      on(te, "compositionend", function() {
        if (input.composing) {
          input.poll();
          input.composing.range.clear();
          input.composing = null;
        }
      });
    },

    prepareSelection: function() {
      // Redraw the selection and/or cursor
      var cm = this.cm, display = cm.display, doc = cm.doc;
      var result = prepareSelection(cm);

      // Move the hidden textarea near the cursor to prevent scrolling artifacts
      if (cm.options.moveInputWithCursor) {
        var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
        var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
        result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
                                            headPos.top + lineOff.top - wrapOff.top));
        result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
                                             headPos.left + lineOff.left - wrapOff.left));
      }

      return result;
    },

    showSelection: function(drawn) {
      var cm = this.cm, display = cm.display;
      removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
      removeChildrenAndAdd(display.selectionDiv, drawn.selection);
      if (drawn.teTop != null) {
        this.wrapper.style.top = drawn.teTop + "px";
        this.wrapper.style.left = drawn.teLeft + "px";
      }
    },

    // Reset the input to correspond to the selection (or to be empty,
    // when not typing and nothing is selected)
    reset: function(typing) {
      if (this.contextMenuPending) return;
      var minimal, selected, cm = this.cm, doc = cm.doc;
      if (cm.somethingSelected()) {
        this.prevInput = "";
        var range = doc.sel.primary();
        minimal = hasCopyEvent &&
          (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000);
        var content = minimal ? "-" : selected || cm.getSelection();
        this.textarea.value = content;
        if (cm.state.focused) selectInput(this.textarea);
        if (ie && ie_version >= 9) this.hasSelection = content;
      } else if (!typing) {
        this.prevInput = this.textarea.value = "";
        if (ie && ie_version >= 9) this.hasSelection = null;
      }
      this.inaccurateSelection = minimal;
    },

    getField: function() { return this.textarea; },

    supportsTouch: function() { return false; },

    focus: function() {
      if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
        try { this.textarea.focus(); }
        catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
      }
    },

    blur: function() { this.textarea.blur(); },

    resetPosition: function() {
      this.wrapper.style.top = this.wrapper.style.left = 0;
    },

    receivedFocus: function() { this.slowPoll(); },

    // Poll for input changes, using the normal rate of polling. This
    // runs as long as the editor is focused.
    slowPoll: function() {
      var input = this;
      if (input.pollingFast) return;
      input.polling.set(this.cm.options.pollInterval, function() {
        input.poll();
        if (input.cm.state.focused) input.slowPoll();
      });
    },

    // When an event has just come in that is likely to add or change
    // something in the input textarea, we poll faster, to ensure that
    // the change appears on the screen quickly.
    fastPoll: function() {
      var missed = false, input = this;
      input.pollingFast = true;
      function p() {
        var changed = input.poll();
        if (!changed && !missed) {missed = true; input.polling.set(60, p);}
        else {input.pollingFast = false; input.slowPoll();}
      }
      input.polling.set(20, p);
    },

    // Read input from the textarea, and update the document to match.
    // When something is selected, it is present in the textarea, and
    // selected (unless it is huge, in which case a placeholder is
    // used). When nothing is selected, the cursor sits after previously
    // seen text (can be empty), which is stored in prevInput (we must
    // not reset the textarea when typing, because that breaks IME).
    poll: function() {
      var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
      // Since this is called a *lot*, try to bail out as cheaply as
      // possible when it is clear that nothing happened. hasSelection
      // will be the case when there is a lot of text in the textarea,
      // in which case reading its value would be expensive.
      if (!cm.state.focused || (hasSelection(input) && !prevInput) ||
          isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
        return false;
      // See paste handler for more on the fakedLastChar kludge
      if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
        input.value = input.value.substring(0, input.value.length - 1);
        cm.state.fakedLastChar = false;
      }
      var text = input.value;
      // If nothing changed, bail.
      if (text == prevInput && !cm.somethingSelected()) return false;
      // Work around nonsensical selection resetting in IE9/10, and
      // inexplicable appearance of private area unicode characters on
      // some key combos in Mac (#2689).
      if (ie && ie_version >= 9 && this.hasSelection === text ||
          mac && /[\uf700-\uf7ff]/.test(text)) {
        cm.display.input.reset();
        return false;
      }

      if (cm.doc.sel == cm.display.selForContextMenu) {
        var first = text.charCodeAt(0);
        if (first == 0x200b && !prevInput) prevInput = "\u200b";
        if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); }
      }
      // Find the part of the input that is actually new
      var same = 0, l = Math.min(prevInput.length, text.length);
      while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;

      var self = this;
      runInOp(cm, function() {
        applyTextInput(cm, text.slice(same), prevInput.length - same,
                       null, self.composing ? "*compose" : null);

        // Don't leave long text in the textarea, since it makes further polling slow
        if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "";
        else self.prevInput = text;

        if (self.composing) {
          self.composing.range.clear();
          self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"),
                                             {className: "CodeMirror-composing"});
        }
      });
      return true;
    },

    ensurePolled: function() {
      if (this.pollingFast && this.poll()) this.pollingFast = false;
    },

    onKeyPress: function() {
      if (ie && ie_version >= 9) this.hasSelection = null;
      this.fastPoll();
    },

    onContextMenu: function(e) {
      var input = this, cm = input.cm, display = cm.display, te = input.textarea;
      var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
      if (!pos || presto) return; // Opera is difficult.

      // Reset the current text selection only if the click is done outside of the selection
      // and 'resetSelectionOnContextMenu' option is true.
      var reset = cm.options.resetSelectionOnContextMenu;
      if (reset && cm.doc.sel.contains(pos) == -1)
        operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);

      var oldCSS = te.style.cssText;
      input.wrapper.style.position = "absolute";
      te.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
        "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
        (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
        "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
      if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
      display.input.focus();
      if (webkit) window.scrollTo(null, oldScrollY);
      display.input.reset();
      // Adds "Select all" to context menu in FF
      if (!cm.somethingSelected()) te.value = input.prevInput = " ";
      input.contextMenuPending = true;
      display.selForContextMenu = cm.doc.sel;
      clearTimeout(display.detectingSelectAll);

      // Select-all will be greyed out if there's nothing to select, so
      // this adds a zero-width space so that we can later check whether
      // it got selected.
      function prepareSelectAllHack() {
        if (te.selectionStart != null) {
          var selected = cm.somethingSelected();
          var extval = "\u200b" + (selected ? te.value : "");
          te.value = "\u21da"; // Used to catch context-menu undo
          te.value = extval;
          input.prevInput = selected ? "" : "\u200b";
          te.selectionStart = 1; te.selectionEnd = extval.length;
          // Re-set this, in case some other handler touched the
          // selection in the meantime.
          display.selForContextMenu = cm.doc.sel;
        }
      }
      function rehide() {
        input.contextMenuPending = false;
        input.wrapper.style.position = "relative";
        te.style.cssText = oldCSS;
        if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos);

        // Try to detect the user choosing select-all
        if (te.selectionStart != null) {
          if (!ie || (ie && ie_version < 9)) prepareSelectAllHack();
          var i = 0, poll = function() {
            if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
                te.selectionEnd > 0 && input.prevInput == "\u200b")
              operation(cm, commands.selectAll)(cm);
            else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500);
            else display.input.reset();
          };
          display.detectingSelectAll = setTimeout(poll, 200);
        }
      }

      if (ie && ie_version >= 9) prepareSelectAllHack();
      if (captureRightClick) {
        e_stop(e);
        var mouseup = function() {
          off(window, "mouseup", mouseup);
          setTimeout(rehide, 20);
        };
        on(window, "mouseup", mouseup);
      } else {
        setTimeout(rehide, 50);
      }
    },

    setUneditable: nothing,

    needsContentAttribute: false
  }, TextareaInput.prototype);

  // CONTENTEDITABLE INPUT STYLE

  function ContentEditableInput(cm) {
    this.cm = cm;
    this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
    this.polling = new Delayed();
    this.gracePeriod = false;
  }

  ContentEditableInput.prototype = copyObj({
    init: function(display) {
      var input = this, cm = input.cm;
      var div = input.div = display.lineDiv;
      div.contentEditable = "true";
      disableBrowserMagic(div);

      on(div, "paste", function(e) {
        var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
        if (pasted) {
          e.preventDefault();
          cm.replaceSelection(pasted, null, "paste");
        }
      });

      on(div, "compositionstart", function(e) {
        var data = e.data;
        input.composing = {sel: cm.doc.sel, data: data, startData: data};
        if (!data) return;
        var prim = cm.doc.sel.primary();
        var line = cm.getLine(prim.head.line);
        var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length));
        if (found > -1 && found <= prim.head.ch)
          input.composing.sel = simpleSelection(Pos(prim.head.line, found),
                                                Pos(prim.head.line, found + data.length));
      });
      on(div, "compositionupdate", function(e) {
        input.composing.data = e.data;
      });
      on(div, "compositionend", function(e) {
        var ours = input.composing;
        if (!ours) return;
        if (e.data != ours.startData && !/\u200b/.test(e.data))
          ours.data = e.data;
        // Need a small delay to prevent other code (input event,
        // selection polling) from doing damage when fired right after
        // compositionend.
        setTimeout(function() {
          if (!ours.handled)
            input.applyComposition(ours);
          if (input.composing == ours)
            input.composing = null;
        }, 50);
      });

      on(div, "touchstart", function() {
        input.forceCompositionEnd();
      });

      on(div, "input", function() {
        if (input.composing) return;
        if (!input.pollContent())
          runInOp(input.cm, function() {regChange(cm);});
      });

      function onCopyCut(e) {
        if (cm.somethingSelected()) {
          lastCopied = cm.getSelections();
          if (e.type == "cut") cm.replaceSelection("", null, "cut");
        } else if (!cm.options.lineWiseCopyCut) {
          return;
        } else {
          var ranges = copyableRanges(cm);
          lastCopied = ranges.text;
          if (e.type == "cut") {
            cm.operation(function() {
              cm.setSelections(ranges.ranges, 0, sel_dontScroll);
              cm.replaceSelection("", null, "cut");
            });
          }
        }
        // iOS exposes the clipboard API, but seems to discard content inserted into it
        if (e.clipboardData && !ios) {
          e.preventDefault();
          e.clipboardData.clearData();
          e.clipboardData.setData("text/plain", lastCopied.join("\n"));
        } else {
          // Old-fashioned briefly-focus-a-textarea hack
          var kludge = hiddenTextarea(), te = kludge.firstChild;
          cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
          te.value = lastCopied.join("\n");
          var hadFocus = document.activeElement;
          selectInput(te);
          setTimeout(function() {
            cm.display.lineSpace.removeChild(kludge);
            hadFocus.focus();
          }, 50);
        }
      }
      on(div, "copy", onCopyCut);
      on(div, "cut", onCopyCut);
    },

    prepareSelection: function() {
      var result = prepareSelection(this.cm, false);
      result.focus = this.cm.state.focused;
      return result;
    },

    showSelection: function(info) {
      if (!info || !this.cm.display.view.length) return;
      if (info.focus) this.showPrimarySelection();
      this.showMultipleSelections(info);
    },

    showPrimarySelection: function() {
      var sel = window.getSelection(), prim = this.cm.doc.sel.primary();
      var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset);
      var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset);
      if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
          cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
          cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
        return;

      var start = posToDOM(this.cm, prim.from());
      var end = posToDOM(this.cm, prim.to());
      if (!start && !end) return;

      var view = this.cm.display.view;
      var old = sel.rangeCount && sel.getRangeAt(0);
      if (!start) {
        start = {node: view[0].measure.map[2], offset: 0};
      } else if (!end) { // FIXME dangerously hacky
        var measure = view[view.length - 1].measure;
        var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
        end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
      }

      try { var rng = range(start.node, start.offset, end.offset, end.node); }
      catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
      if (rng) {
        sel.removeAllRanges();
        sel.addRange(rng);
        if (old && sel.anchorNode == null) sel.addRange(old);
        else if (gecko) this.startGracePeriod();
      }
      this.rememberSelection();
    },

    startGracePeriod: function() {
      var input = this;
      clearTimeout(this.gracePeriod);
      this.gracePeriod = setTimeout(function() {
        input.gracePeriod = false;
        if (input.selectionChanged())
          input.cm.operation(function() { input.cm.curOp.selectionChanged = true; });
      }, 20);
    },

    showMultipleSelections: function(info) {
      removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
      removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
    },

    rememberSelection: function() {
      var sel = window.getSelection();
      this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
      this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
    },

    selectionInEditor: function() {
      var sel = window.getSelection();
      if (!sel.rangeCount) return false;
      var node = sel.getRangeAt(0).commonAncestorContainer;
      return contains(this.div, node);
    },

    focus: function() {
      if (this.cm.options.readOnly != "nocursor") this.div.focus();
    },
    blur: function() { this.div.blur(); },
    getField: function() { return this.div; },

    supportsTouch: function() { return true; },

    receivedFocus: function() {
      var input = this;
      if (this.selectionInEditor())
        this.pollSelection();
      else
        runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; });

      function poll() {
        if (input.cm.state.focused) {
          input.pollSelection();
          input.polling.set(input.cm.options.pollInterval, poll);
        }
      }
      this.polling.set(this.cm.options.pollInterval, poll);
    },

    selectionChanged: function() {
      var sel = window.getSelection();
      return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
        sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset;
    },

    pollSelection: function() {
      if (!this.composing && !this.gracePeriod && this.selectionChanged()) {
        var sel = window.getSelection(), cm = this.cm;
        this.rememberSelection();
        var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
        var head = domToPos(cm, sel.focusNode, sel.focusOffset);
        if (anchor && head) runInOp(cm, function() {
          setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
          if (anchor.bad || head.bad) cm.curOp.selectionChanged = true;
        });
      }
    },

    pollContent: function() {
      var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
      var from = sel.from(), to = sel.to();
      if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false;

      var fromIndex;
      if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
        var fromLine = lineNo(display.view[0].line);
        var fromNode = display.view[0].node;
      } else {
        var fromLine = lineNo(display.view[fromIndex].line);
        var fromNode = display.view[fromIndex - 1].node.nextSibling;
      }
      var toIndex = findViewIndex(cm, to.line);
      if (toIndex == display.view.length - 1) {
        var toLine = display.viewTo - 1;
        var toNode = display.view[toIndex].node;
      } else {
        var toLine = lineNo(display.view[toIndex + 1].line) - 1;
        var toNode = display.view[toIndex + 1].node.previousSibling;
      }

      var newText = splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
      var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
      while (newText.length > 1 && oldText.length > 1) {
        if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
        else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
        else break;
      }

      var cutFront = 0, cutEnd = 0;
      var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
      while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
        ++cutFront;
      var newBot = lst(newText), oldBot = lst(oldText);
      var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
                               oldBot.length - (oldText.length == 1 ? cutFront : 0));
      while (cutEnd < maxCutEnd &&
             newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
        ++cutEnd;

      newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd);
      newText[0] = newText[0].slice(cutFront);

      var chFrom = Pos(fromLine, cutFront);
      var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
      if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
        replaceRange(cm.doc, newText, chFrom, chTo, "+input");
        return true;
      }
    },

    ensurePolled: function() {
      this.forceCompositionEnd();
    },
    reset: function() {
      this.forceCompositionEnd();
    },
    forceCompositionEnd: function() {
      if (!this.composing || this.composing.handled) return;
      this.applyComposition(this.composing);
      this.composing.handled = true;
      this.div.blur();
      this.div.focus();
    },
    applyComposition: function(composing) {
      if (composing.data && composing.data != composing.startData)
        operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel);
    },

    setUneditable: function(node) {
      node.setAttribute("contenteditable", "false");
    },

    onKeyPress: function(e) {
      e.preventDefault();
      operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
    },

    onContextMenu: nothing,
    resetPosition: nothing,

    needsContentAttribute: true
  }, ContentEditableInput.prototype);

  function posToDOM(cm, pos) {
    var view = findViewForLine(cm, pos.line);
    if (!view || view.hidden) return null;
    var line = getLine(cm.doc, pos.line);
    var info = mapFromLineView(view, line, pos.line);

    var order = getOrder(line), side = "left";
    if (order) {
      var partPos = getBidiPartAt(order, pos.ch);
      side = partPos % 2 ? "right" : "left";
    }
    var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
    result.offset = result.collapse == "right" ? result.end : result.start;
    return result;
  }

  function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }

  function domToPos(cm, node, offset) {
    var lineNode;
    if (node == cm.display.lineDiv) {
      lineNode = cm.display.lineDiv.childNodes[offset];
      if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true);
      node = null; offset = 0;
    } else {
      for (lineNode = node;; lineNode = lineNode.parentNode) {
        if (!lineNode || lineNode == cm.display.lineDiv) return null;
        if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break;
      }
    }
    for (var i = 0; i < cm.display.view.length; i++) {
      var lineView = cm.display.view[i];
      if (lineView.node == lineNode)
        return locateNodeInLineView(lineView, node, offset);
    }
  }

  function locateNodeInLineView(lineView, node, offset) {
    var wrapper = lineView.text.firstChild, bad = false;
    if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true);
    if (node == wrapper) {
      bad = true;
      node = wrapper.childNodes[offset];
      offset = 0;
      if (!node) {
        var line = lineView.rest ? lst(lineView.rest) : lineView.line;
        return badPos(Pos(lineNo(line), line.text.length), bad);
      }
    }

    var textNode = node.nodeType == 3 ? node : null, topNode = node;
    if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
      textNode = node.firstChild;
      if (offset) offset = textNode.nodeValue.length;
    }
    while (topNode.parentNode != wrapper) topNode = topNode.parentNode;
    var measure = lineView.measure, maps = measure.maps;

    function find(textNode, topNode, offset) {
      for (var i = -1; i < (maps ? maps.length : 0); i++) {
        var map = i < 0 ? measure.map : maps[i];
        for (var j = 0; j < map.length; j += 3) {
          var curNode = map[j + 2];
          if (curNode == textNode || curNode == topNode) {
            var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
            var ch = map[j] + offset;
            if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)];
            return Pos(line, ch);
          }
        }
      }
    }
    var found = find(textNode, topNode, offset);
    if (found) return badPos(found, bad);

    // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
    for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
      found = find(after, after.firstChild, 0);
      if (found)
        return badPos(Pos(found.line, found.ch - dist), bad);
      else
        dist += after.textContent.length;
    }
    for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) {
      found = find(before, before.firstChild, -1);
      if (found)
        return badPos(Pos(found.line, found.ch + dist), bad);
      else
        dist += after.textContent.length;
    }
  }

  function domTextBetween(cm, from, to, fromLine, toLine) {
    var text = "", closing = false;
    function recognizeMarker(id) { return function(marker) { return marker.id == id; }; }
    function walk(node) {
      if (node.nodeType == 1) {
        var cmText = node.getAttribute("cm-text");
        if (cmText != null) {
          if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "");
          text += cmText;
          return;
        }
        var markerID = node.getAttribute("cm-marker"), range;
        if (markerID) {
          var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
          if (found.length && (range = found[0].find()))
            text += getBetween(cm.doc, range.from, range.to).join("\n");
          return;
        }
        if (node.getAttribute("contenteditable") == "false") return;
        for (var i = 0; i < node.childNodes.length; i++)
          walk(node.childNodes[i]);
        if (/^(pre|div|p)$/i.test(node.nodeName))
          closing = true;
      } else if (node.nodeType == 3) {
        var val = node.nodeValue;
        if (!val) return;
        if (closing) {
          text += "\n";
          closing = false;
        }
        text += val;
      }
    }
    for (;;) {
      walk(from);
      if (from == to) break;
      from = from.nextSibling;
    }
    return text;
  }

  CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};

  // SELECTION / CURSOR

  // Selection objects are immutable. A new one is created every time
  // the selection changes. A selection is one or more non-overlapping
  // (and non-touching) ranges, sorted, and an integer that indicates
  // which one is the primary selection (the one that's scrolled into
  // view, that getCursor returns, etc).
  function Selection(ranges, primIndex) {
    this.ranges = ranges;
    this.primIndex = primIndex;
  }

  Selection.prototype = {
    primary: function() { return this.ranges[this.primIndex]; },
    equals: function(other) {
      if (other == this) return true;
      if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
      for (var i = 0; i < this.ranges.length; i++) {
        var here = this.ranges[i], there = other.ranges[i];
        if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
      }
      return true;
    },
    deepCopy: function() {
      for (var out = [], i = 0; i < this.ranges.length; i++)
        out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
      return new Selection(out, this.primIndex);
    },
    somethingSelected: function() {
      for (var i = 0; i < this.ranges.length; i++)
        if (!this.ranges[i].empty()) return true;
      return false;
    },
    contains: function(pos, end) {
      if (!end) end = pos;
      for (var i = 0; i < this.ranges.length; i++) {
        var range = this.ranges[i];
        if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
          return i;
      }
      return -1;
    }
  };

  function Range(anchor, head) {
    this.anchor = anchor; this.head = head;
  }

  Range.prototype = {
    from: function() { return minPos(this.anchor, this.head); },
    to: function() { return maxPos(this.anchor, this.head); },
    empty: function() {
      return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch;
    }
  };

  // Take an unsorted, potentially overlapping set of ranges, and
  // build a selection out of it. 'Consumes' ranges array (modifying
  // it).
  function normalizeSelection(ranges, primIndex) {
    var prim = ranges[primIndex];
    ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
    primIndex = indexOf(ranges, prim);
    for (var i = 1; i < ranges.length; i++) {
      var cur = ranges[i], prev = ranges[i - 1];
      if (cmp(prev.to(), cur.from()) >= 0) {
        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
        if (i <= primIndex) --primIndex;
        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
      }
    }
    return new Selection(ranges, primIndex);
  }

  function simpleSelection(anchor, head) {
    return new Selection([new Range(anchor, head || anchor)], 0);
  }

  // Most of the external API clips given positions to make sure they
  // actually exist within the document.
  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
  function clipPos(doc, pos) {
    if (pos.line < doc.first) return Pos(doc.first, 0);
    var last = doc.first + doc.size - 1;
    if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
    return clipToLen(pos, getLine(doc, pos.line).text.length);
  }
  function clipToLen(pos, linelen) {
    var ch = pos.ch;
    if (ch == null || ch > linelen) return Pos(pos.line, linelen);
    else if (ch < 0) return Pos(pos.line, 0);
    else return pos;
  }
  function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
  function clipPosArray(doc, array) {
    for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]);
    return out;
  }

  // SELECTION UPDATES

  // The 'scroll' parameter given to many of these indicated whether
  // the new cursor position should be scrolled into view after
  // modifying the selection.

  // If shift is held or the extend flag is set, extends a range to
  // include a given position (and optionally a second position).
  // Otherwise, simply returns the range between the given positions.
  // Used for cursor motion and such.
  function extendRange(doc, range, head, other) {
    if (doc.cm && doc.cm.display.shift || doc.extend) {
      var anchor = range.anchor;
      if (other) {
        var posBefore = cmp(head, anchor) < 0;
        if (posBefore != (cmp(other, anchor) < 0)) {
          anchor = head;
          head = other;
        } else if (posBefore != (cmp(head, other) < 0)) {
          head = other;
        }
      }
      return new Range(anchor, head);
    } else {
      return new Range(other || head, head);
    }
  }

  // Extend the primary selection range, discard the rest.
  function extendSelection(doc, head, other, options) {
    setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
  }

  // Extend all selections (pos is an array of selections with length
  // equal the number of selections)
  function extendSelections(doc, heads, options) {
    for (var out = [], i = 0; i < doc.sel.ranges.length; i++)
      out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
    var newSel = normalizeSelection(out, doc.sel.primIndex);
    setSelection(doc, newSel, options);
  }

  // Updates a single range in the selection.
  function replaceOneSelection(doc, i, range, options) {
    var ranges = doc.sel.ranges.slice(0);
    ranges[i] = range;
    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
  }

  // Reset the selection to a single range.
  function setSimpleSelection(doc, anchor, head, options) {
    setSelection(doc, simpleSelection(anchor, head), options);
  }

  // Give beforeSelectionChange handlers a change to influence a
  // selection update.
  function filterSelectionChange(doc, sel) {
    var obj = {
      ranges: sel.ranges,
      update: function(ranges) {
        this.ranges = [];
        for (var i = 0; i < ranges.length; i++)
          this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
                                     clipPos(doc, ranges[i].head));
      }
    };
    signal(doc, "beforeSelectionChange", doc, obj);
    if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
    if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
    else return sel;
  }

  function setSelectionReplaceHistory(doc, sel, options) {
    var done = doc.history.done, last = lst(done);
    if (last && last.ranges) {
      done[done.length - 1] = sel;
      setSelectionNoUndo(doc, sel, options);
    } else {
      setSelection(doc, sel, options);
    }
  }

  // Set a new selection.
  function setSelection(doc, sel, options) {
    setSelectionNoUndo(doc, sel, options);
    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
  }

  function setSelectionNoUndo(doc, sel, options) {
    if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
      sel = filterSelectionChange(doc, sel);

    var bias = options && options.bias ||
      (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));

    if (!(options && options.scroll === false) && doc.cm)
      ensureCursorVisible(doc.cm);
  }

  function setSelectionInner(doc, sel) {
    if (sel.equals(doc.sel)) return;

    doc.sel = sel;

    if (doc.cm) {
      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
      signalCursorActivity(doc.cm);
    }
    signalLater(doc, "cursorActivity", doc);
  }

  // Verify that the selection does not partially select any atomic
  // marked ranges.
  function reCheckSelection(doc) {
    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
  }

  // Return a selection that does not partially select any atomic
  // ranges.
  function skipAtomicInSelection(doc, sel, bias, mayClear) {
    var out;
    for (var i = 0; i < sel.ranges.length; i++) {
      var range = sel.ranges[i];
      var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
      var newHead = skipAtomic(doc, range.head, bias, mayClear);
      if (out || newAnchor != range.anchor || newHead != range.head) {
        if (!out) out = sel.ranges.slice(0, i);
        out[i] = new Range(newAnchor, newHead);
      }
    }
    return out ? normalizeSelection(out, sel.primIndex) : sel;
  }

  // Ensure a given position is not inside an atomic range.
  function skipAtomic(doc, pos, bias, mayClear) {
    var flipped = false, curPos = pos;
    var dir = bias || 1;
    doc.cantEdit = false;
    search: for (;;) {
      var line = getLine(doc, curPos.line);
      if (line.markedSpans) {
        for (var i = 0; i < line.markedSpans.length; ++i) {
          var sp = line.markedSpans[i], m = sp.marker;
          if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
              (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
            if (mayClear) {
              signal(m, "beforeCursorEnter");
              if (m.explicitlyCleared) {
                if (!line.markedSpans) break;
                else {--i; continue;}
              }
            }
            if (!m.atomic) continue;
            var newPos = m.find(dir < 0 ? -1 : 1);
            if (cmp(newPos, curPos) == 0) {
              newPos.ch += dir;
              if (newPos.ch < 0) {
                if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
                else newPos = null;
              } else if (newPos.ch > line.text.length) {
                if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
                else newPos = null;
              }
              if (!newPos) {
                if (flipped) {
                  // Driven in a corner -- no valid cursor position found at all
                  // -- try again *with* clearing, if we didn't already
                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
                  // Otherwise, turn off editing until further notice, and return the start of the doc
                  doc.cantEdit = true;
                  return Pos(doc.first, 0);
                }
                flipped = true; newPos = pos; dir = -dir;
              }
            }
            curPos = newPos;
            continue search;
          }
        }
      }
      return curPos;
    }
  }

  // SELECTION DRAWING

  function updateSelection(cm) {
    cm.display.input.showSelection(cm.display.input.prepareSelection());
  }

  function prepareSelection(cm, primary) {
    var doc = cm.doc, result = {};
    var curFragment = result.cursors = document.createDocumentFragment();
    var selFragment = result.selection = document.createDocumentFragment();

    for (var i = 0; i < doc.sel.ranges.length; i++) {
      if (primary === false && i == doc.sel.primIndex) continue;
      var range = doc.sel.ranges[i];
      var collapsed = range.empty();
      if (collapsed || cm.options.showCursorWhenSelecting)
        drawSelectionCursor(cm, range, curFragment);
      if (!collapsed)
        drawSelectionRange(cm, range, selFragment);
    }
    return result;
  }

  // Draws a cursor for the given range
  function drawSelectionCursor(cm, range, output) {
    var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.singleCursorHeightPerLine);

    var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
    cursor.style.left = pos.left + "px";
    cursor.style.top = pos.top + "px";
    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";

    if (pos.other) {
      // Secondary cursor, shown when on a 'jump' in bi-directional text
      var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
      otherCursor.style.display = "";
      otherCursor.style.left = pos.other.left + "px";
      otherCursor.style.top = pos.other.top + "px";
      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
    }
  }

  // Draws the given range as a highlighted selection
  function drawSelectionRange(cm, range, output) {
    var display = cm.display, doc = cm.doc;
    var fragment = document.createDocumentFragment();
    var padding = paddingH(cm.display), leftSide = padding.left;
    var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;

    function add(left, top, width, bottom) {
      if (top < 0) top = 0;
      top = Math.round(top);
      bottom = Math.round(bottom);
      fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
                               "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) +
                               "px; height: " + (bottom - top) + "px"));
    }

    function drawForLine(line, fromArg, toArg) {
      var lineObj = getLine(doc, line);
      var lineLen = lineObj.text.length;
      var start, end;
      function coords(ch, bias) {
        return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
      }

      iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
        var leftPos = coords(from, "left"), rightPos, left, right;
        if (from == to) {
          rightPos = leftPos;
          left = right = leftPos.left;
        } else {
          rightPos = coords(to - 1, "right");
          if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
          left = leftPos.left;
          right = rightPos.right;
        }
        if (fromArg == null && from == 0) left = leftSide;
        if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
          add(left, leftPos.top, null, leftPos.bottom);
          left = leftSide;
          if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
        }
        if (toArg == null && to == lineLen) right = rightSide;
        if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
          start = leftPos;
        if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
          end = rightPos;
        if (left < leftSide + 1) left = leftSide;
        add(left, rightPos.top, right - left, rightPos.bottom);
      });
      return {start: start, end: end};
    }

    var sFrom = range.from(), sTo = range.to();
    if (sFrom.line == sTo.line) {
      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
    } else {
      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
      var singleVLine = visualLine(fromLine) == visualLine(toLine);
      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
      if (singleVLine) {
        if (leftEnd.top < rightStart.top - 2) {
          add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
        } else {
          add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
        }
      }
      if (leftEnd.bottom < rightStart.top)
        add(leftSide, leftEnd.bottom, null, rightStart.top);
    }

    output.appendChild(fragment);
  }

  // Cursor-blinking
  function restartBlink(cm) {
    if (!cm.state.focused) return;
    var display = cm.display;
    clearInterval(display.blinker);
    var on = true;
    display.cursorDiv.style.visibility = "";
    if (cm.options.cursorBlinkRate > 0)
      display.blinker = setInterval(function() {
        display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
      }, cm.options.cursorBlinkRate);
    else if (cm.options.cursorBlinkRate < 0)
      display.cursorDiv.style.visibility = "hidden";
  }

  // HIGHLIGHT WORKER

  function startWorker(cm, time) {
    if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
      cm.state.highlight.set(time, bind(highlightWorker, cm));
  }

  function highlightWorker(cm) {
    var doc = cm.doc;
    if (doc.frontier < doc.first) doc.frontier = doc.first;
    if (doc.frontier >= cm.display.viewTo) return;
    var end = +new Date + cm.options.workTime;
    var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
    var changedLines = [];

    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
      if (doc.frontier >= cm.display.viewFrom) { // Visible
        var oldStyles = line.styles;
        var highlighted = highlightLine(cm, line, state, true);
        line.styles = highlighted.styles;
        var oldCls = line.styleClasses, newCls = highlighted.classes;
        if (newCls) line.styleClasses = newCls;
        else if (oldCls) line.styleClasses = null;
        var ischange = !oldStyles || oldStyles.length != line.styles.length ||
          oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
        for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
        if (ischange) changedLines.push(doc.frontier);
        line.stateAfter = copyState(doc.mode, state);
      } else {
        processLine(cm, line.text, state);
        line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
      }
      ++doc.frontier;
      if (+new Date > end) {
        startWorker(cm, cm.options.workDelay);
        return true;
      }
    });
    if (changedLines.length) runInOp(cm, function() {
      for (var i = 0; i < changedLines.length; i++)
        regLineChange(cm, changedLines[i], "text");
    });
  }

  // Finds the line to start with when starting a parse. Tries to
  // find a line with a stateAfter, so that it can start with a
  // valid state. If that fails, it returns the line with the
  // smallest indentation, which tends to need the least context to
  // parse correctly.
  function findStartLine(cm, n, precise) {
    var minindent, minline, doc = cm.doc;
    var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
    for (var search = n; search > lim; --search) {
      if (search <= doc.first) return doc.first;
      var line = getLine(doc, search - 1);
      if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
      var indented = countColumn(line.text, null, cm.options.tabSize);
      if (minline == null || minindent > indented) {
        minline = search - 1;
        minindent = indented;
      }
    }
    return minline;
  }

  function getStateBefore(cm, n, precise) {
    var doc = cm.doc, display = cm.display;
    if (!doc.mode.startState) return true;
    var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
    if (!state) state = startState(doc.mode);
    else state = copyState(doc.mode, state);
    doc.iter(pos, n, function(line) {
      processLine(cm, line.text, state);
      var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo;
      line.stateAfter = save ? copyState(doc.mode, state) : null;
      ++pos;
    });
    if (precise) doc.frontier = pos;
    return state;
  }

  // POSITION MEASUREMENT

  function paddingTop(display) {return display.lineSpace.offsetTop;}
  function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
  function paddingH(display) {
    if (display.cachedPaddingH) return display.cachedPaddingH;
    var e = removeChildrenAndAdd(display.measure, elt("pre", "x"));
    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
    var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
    if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data;
    return data;
  }

  function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
  function displayWidth(cm) {
    return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth;
  }
  function displayHeight(cm) {
    return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight;
  }

  // Ensure the lineView.wrapping.heights array is populated. This is
  // an array of bottom offsets for the lines that make up a drawn
  // line. When lineWrapping is on, there might be more than one
  // height.
  function ensureLineHeights(cm, lineView, rect) {
    var wrapping = cm.options.lineWrapping;
    var curWidth = wrapping && displayWidth(cm);
    if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
      var heights = lineView.measure.heights = [];
      if (wrapping) {
        lineView.measure.width = curWidth;
        var rects = lineView.text.firstChild.getClientRects();
        for (var i = 0; i < rects.length - 1; i++) {
          var cur = rects[i], next = rects[i + 1];
          if (Math.abs(cur.bottom - next.bottom) > 2)
            heights.push((cur.bottom + next.top) / 2 - rect.top);
        }
      }
      heights.push(rect.bottom - rect.top);
    }
  }

  // Find a line map (mapping character offsets to text nodes) and a
  // measurement cache for the given line number. (A line view might
  // contain multiple lines when collapsed ranges are present.)
  function mapFromLineView(lineView, line, lineN) {
    if (lineView.line == line)
      return {map: lineView.measure.map, cache: lineView.measure.cache};
    for (var i = 0; i < lineView.rest.length; i++)
      if (lineView.rest[i] == line)
        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
    for (var i = 0; i < lineView.rest.length; i++)
      if (lineNo(lineView.rest[i]) > lineN)
        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
  }

  // Render a line into the hidden node display.externalMeasured. Used
  // when measurement is needed for a line that's not in the viewport.
  function updateExternalMeasurement(cm, line) {
    line = visualLine(line);
    var lineN = lineNo(line);
    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
    view.lineN = lineN;
    var built = view.built = buildLineContent(cm, view);
    view.text = built.pre;
    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
    return view;
  }

  // Get a {top, bottom, left, right} box (in line-local coordinates)
  // for a given character.
  function measureChar(cm, line, ch, bias) {
    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
  }

  // Find a line view that corresponds to the given line number.
  function findViewForLine(cm, lineN) {
    if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
      return cm.display.view[findViewIndex(cm, lineN)];
    var ext = cm.display.externalMeasured;
    if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
      return ext;
  }

  // Measurement can be split in two steps, the set-up work that
  // applies to the whole line, and the measurement of the actual
  // character. Functions like coordsChar, that need to do a lot of
  // measurements in a row, can thus ensure that the set-up work is
  // only done once.
  function prepareMeasureForLine(cm, line) {
    var lineN = lineNo(line);
    var view = findViewForLine(cm, lineN);
    if (view && !view.text)
      view = null;
    else if (view && view.changes)
      updateLineForChanges(cm, view, lineN, getDimensions(cm));
    if (!view)
      view = updateExternalMeasurement(cm, line);

    var info = mapFromLineView(view, line, lineN);
    return {
      line: line, view: view, rect: null,
      map: info.map, cache: info.cache, before: info.before,
      hasHeights: false
    };
  }

  // Given a prepared measurement object, measures the position of an
  // actual character (or fetches it from the cache).
  function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
    if (prepared.before) ch = -1;
    var key = ch + (bias || ""), found;
    if (prepared.cache.hasOwnProperty(key)) {
      found = prepared.cache[key];
    } else {
      if (!prepared.rect)
        prepared.rect = prepared.view.text.getBoundingClientRect();
      if (!prepared.hasHeights) {
        ensureLineHeights(cm, prepared.view, prepared.rect);
        prepared.hasHeights = true;
      }
      found = measureCharInner(cm, prepared, ch, bias);
      if (!found.bogus) prepared.cache[key] = found;
    }
    return {left: found.left, right: found.right,
            top: varHeight ? found.rtop : found.top,
            bottom: varHeight ? found.rbottom : found.bottom};
  }

  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};

  function nodeAndOffsetInLineMap(map, ch, bias) {
    var node, start, end, collapse;
    // First, search the line map for the text node corresponding to,
    // or closest to, the target character.
    for (var i = 0; i < map.length; i += 3) {
      var mStart = map[i], mEnd = map[i + 1];
      if (ch < mStart) {
        start = 0; end = 1;
        collapse = "left";
      } else if (ch < mEnd) {
        start = ch - mStart;
        end = start + 1;
      } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
        end = mEnd - mStart;
        start = end - 1;
        if (ch >= mEnd) collapse = "right";
      }
      if (start != null) {
        node = map[i + 2];
        if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
          collapse = bias;
        if (bias == "left" && start == 0)
          while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
            node = map[(i -= 3) + 2];
            collapse = "left";
          }
        if (bias == "right" && start == mEnd - mStart)
          while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
            node = map[(i += 3) + 2];
            collapse = "right";
          }
        break;
      }
    }
    return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd};
  }

  function measureCharInner(cm, prepared, ch, bias) {
    var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
    var node = place.node, start = place.start, end = place.end, collapse = place.collapse;

    var rect;
    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
      for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
        while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start;
        while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end;
        if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) {
          rect = node.parentNode.getBoundingClientRect();
        } else if (ie && cm.options.lineWrapping) {
          var rects = range(node, start, end).getClientRects();
          if (rects.length)
            rect = rects[bias == "right" ? rects.length - 1 : 0];
          else
            rect = nullRect;
        } else {
          rect = range(node, start, end).getBoundingClientRect() || nullRect;
        }
        if (rect.left || rect.right || start == 0) break;
        end = start;
        start = start - 1;
        collapse = "right";
      }
      if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
    } else { // If it is a widget, simply get the box for the whole widget.
      if (start > 0) collapse = bias = "right";
      var rects;
      if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
        rect = rects[bias == "right" ? rects.length - 1 : 0];
      else
        rect = node.getBoundingClientRect();
    }
    if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
      var rSpan = node.parentNode.getClientRects()[0];
      if (rSpan)
        rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
      else
        rect = nullRect;
    }

    var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
    var mid = (rtop + rbot) / 2;
    var heights = prepared.view.measure.heights;
    for (var i = 0; i < heights.length - 1; i++)
      if (mid < heights[i]) break;
    var top = i ? heights[i - 1] : 0, bot = heights[i];
    var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
                  right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
                  top: top, bottom: bot};
    if (!rect.left && !rect.right) result.bogus = true;
    if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }

    return result;
  }

  // Work around problem with bounding client rects on ranges being
  // returned incorrectly when zoomed on IE10 and below.
  function maybeUpdateRectForZooming(measure, rect) {
    if (!window.screen || screen.logicalXDPI == null ||
        screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
      return rect;
    var scaleX = screen.logicalXDPI / screen.deviceXDPI;
    var scaleY = screen.logicalYDPI / screen.deviceYDPI;
    return {left: rect.left * scaleX, right: rect.right * scaleX,
            top: rect.top * scaleY, bottom: rect.bottom * scaleY};
  }

  function clearLineMeasurementCacheFor(lineView) {
    if (lineView.measure) {
      lineView.measure.cache = {};
      lineView.measure.heights = null;
      if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
        lineView.measure.caches[i] = {};
    }
  }

  function clearLineMeasurementCache(cm) {
    cm.display.externalMeasure = null;
    removeChildren(cm.display.lineMeasure);
    for (var i = 0; i < cm.display.view.length; i++)
      clearLineMeasurementCacheFor(cm.display.view[i]);
  }

  function clearCaches(cm) {
    clearLineMeasurementCache(cm);
    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
    if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
    cm.display.lineNumChars = null;
  }

  function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
  function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }

  // Converts a {top, bottom, left, right} box from line-local
  // coordinates into another coordinate system. Context may be one of
  // "line", "div" (display.lineDiv), "local"/null (editor), "window",
  // or "page".
  function intoCoordSystem(cm, lineObj, rect, context) {
    if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
      var size = widgetHeight(lineObj.widgets[i]);
      rect.top += size; rect.bottom += size;
    }
    if (context == "line") return rect;
    if (!context) context = "local";
    var yOff = heightAtLine(lineObj);
    if (context == "local") yOff += paddingTop(cm.display);
    else yOff -= cm.display.viewOffset;
    if (context == "page" || context == "window") {
      var lOff = cm.display.lineSpace.getBoundingClientRect();
      yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
      var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
      rect.left += xOff; rect.right += xOff;
    }
    rect.top += yOff; rect.bottom += yOff;
    return rect;
  }

  // Coverts a box from "div" coords to another coordinate system.
  // Context may be "window", "page", "div", or "local"/null.
  function fromCoordSystem(cm, coords, context) {
    if (context == "div") return coords;
    var left = coords.left, top = coords.top;
    // First move into "page" coordinate system
    if (context == "page") {
      left -= pageScrollX();
      top -= pageScrollY();
    } else if (context == "local" || !context) {
      var localBox = cm.display.sizer.getBoundingClientRect();
      left += localBox.left;
      top += localBox.top;
    }

    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
    return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
  }

  function charCoords(cm, pos, context, lineObj, bias) {
    if (!lineObj) lineObj = getLine(cm.doc, pos.line);
    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
  }

  // Returns a box for a given cursor position, which may have an
  // 'other' property containing the position of the secondary cursor
  // on a bidi boundary.
  function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
    lineObj = lineObj || getLine(cm.doc, pos.line);
    if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
    function get(ch, right) {
      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
      if (right) m.left = m.right; else m.right = m.left;
      return intoCoordSystem(cm, lineObj, m, context);
    }
    function getBidi(ch, partPos) {
      var part = order[partPos], right = part.level % 2;
      if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
        part = order[--partPos];
        ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
        right = true;
      } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
        part = order[++partPos];
        ch = bidiLeft(part) - part.level % 2;
        right = false;
      }
      if (right && ch == part.to && ch > part.from) return get(ch - 1);
      return get(ch, right);
    }
    var order = getOrder(lineObj), ch = pos.ch;
    if (!order) return get(ch);
    var partPos = getBidiPartAt(order, ch);
    var val = getBidi(ch, partPos);
    if (bidiOther != null) val.other = getBidi(ch, bidiOther);
    return val;
  }

  // Used to cheaply estimate the coordinates for a position. Used for
  // intermediate scroll updates.
  function estimateCoords(cm, pos) {
    var left = 0, pos = clipPos(cm.doc, pos);
    if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
    var lineObj = getLine(cm.doc, pos.line);
    var top = heightAtLine(lineObj) + paddingTop(cm.display);
    return {left: left, right: left, top: top, bottom: top + lineObj.height};
  }

  // Positions returned by coordsChar contain some extra information.
  // xRel is the relative x position of the input coordinates compared
  // to the found position (so xRel > 0 means the coordinates are to
  // the right of the character position, for example). When outside
  // is true, that means the coordinates lie outside the line's
  // vertical range.
  function PosWithInfo(line, ch, outside, xRel) {
    var pos = Pos(line, ch);
    pos.xRel = xRel;
    if (outside) pos.outside = true;
    return pos;
  }

  // Compute the character position closest to the given coordinates.
  // Input must be lineSpace-local ("div" coordinate system).
  function coordsChar(cm, x, y) {
    var doc = cm.doc;
    y += cm.display.viewOffset;
    if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
    if (lineN > last)
      return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
    if (x < 0) x = 0;

    var lineObj = getLine(doc, lineN);
    for (;;) {
      var found = coordsCharInner(cm, lineObj, lineN, x, y);
      var merged = collapsedSpanAtEnd(lineObj);
      var mergedPos = merged && merged.find(0, true);
      if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
        lineN = lineNo(lineObj = mergedPos.to.line);
      else
        return found;
    }
  }

  function coordsCharInner(cm, lineObj, lineNo, x, y) {
    var innerOff = y - heightAtLine(lineObj);
    var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
    var preparedMeasure = prepareMeasureForLine(cm, lineObj);

    function getX(ch) {
      var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure);
      wrongLine = true;
      if (innerOff > sp.bottom) return sp.left - adjust;
      else if (innerOff < sp.top) return sp.left + adjust;
      else wrongLine = false;
      return sp.left;
    }

    var bidi = getOrder(lineObj), dist = lineObj.text.length;
    var from = lineLeft(lineObj), to = lineRight(lineObj);
    var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;

    if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
    // Do a binary search between these bounds.
    for (;;) {
      if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
        var ch = x < fromX || x - fromX <= toX - x ? from : to;
        var xDiff = x - (ch == from ? fromX : toX);
        while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
        var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
                              xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0);
        return pos;
      }
      var step = Math.ceil(dist / 2), middle = from + step;
      if (bidi) {
        middle = from;
        for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
      }
      var middleX = getX(middle);
      if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
      else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
    }
  }

  var measureText;
  // Compute the default text height.
  function textHeight(display) {
    if (display.cachedTextHeight != null) return display.cachedTextHeight;
    if (measureText == null) {
      measureText = elt("pre");
      // Measure a bunch of lines, for browsers that compute
      // fractional heights.
      for (var i = 0; i < 49; ++i) {
        measureText.appendChild(document.createTextNode("x"));
        measureText.appendChild(elt("br"));
      }
      measureText.appendChild(document.createTextNode("x"));
    }
    removeChildrenAndAdd(display.measure, measureText);
    var height = measureText.offsetHeight / 50;
    if (height > 3) display.cachedTextHeight = height;
    removeChildren(display.measure);
    return height || 1;
  }

  // Compute the default character width.
  function charWidth(display) {
    if (display.cachedCharWidth != null) return display.cachedCharWidth;
    var anchor = elt("span", "xxxxxxxxxx");
    var pre = elt("pre", [anchor]);
    removeChildrenAndAdd(display.measure, pre);
    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
    if (width > 2) display.cachedCharWidth = width;
    return width || 10;
  }

  // OPERATIONS

  // Operations are used to wrap a series of changes to the editor
  // state in such a way that each change won't have to update the
  // cursor and display (which would be awkward, slow, and
  // error-prone). Instead, display updates are batched and then all
  // combined and executed at once.

  var operationGroup = null;

  var nextOpId = 0;
  // Start a new operation.
  function startOperation(cm) {
    cm.curOp = {
      cm: cm,
      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
      startHeight: cm.doc.height, // Used to detect need to update scrollbar
      forceUpdate: false,      // Used to force a redraw
      updateInput: null,       // Whether to reset the input textarea
      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
      changeObjs: null,        // Accumulated changes, for firing change events
      cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
      cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
      selectionChanged: false, // Whether the selection needs to be redrawn
      updateMaxLine: false,    // Set when the widest line needs to be determined anew
      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
      scrollToPos: null,       // Used to scroll to a specific position
      focus: false,
      id: ++nextOpId           // Unique ID
    };
    if (operationGroup) {
      operationGroup.ops.push(cm.curOp);
    } else {
      cm.curOp.ownsGroup = operationGroup = {
        ops: [cm.curOp],
        delayedCallbacks: []
      };
    }
  }

  function fireCallbacksForOps(group) {
    // Calls delayed callbacks and cursorActivity handlers until no
    // new ones appear
    var callbacks = group.delayedCallbacks, i = 0;
    do {
      for (; i < callbacks.length; i++)
        callbacks[i]();
      for (var j = 0; j < group.ops.length; j++) {
        var op = group.ops[j];
        if (op.cursorActivityHandlers)
          while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
            op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
      }
    } while (i < callbacks.length);
  }

  // Finish an operation, updating the display and signalling delayed events
  function endOperation(cm) {
    var op = cm.curOp, group = op.ownsGroup;
    if (!group) return;

    try { fireCallbacksForOps(group); }
    finally {
      operationGroup = null;
      for (var i = 0; i < group.ops.length; i++)
        group.ops[i].cm.curOp = null;
      endOperations(group);
    }
  }

  // The DOM updates done when an operation finishes are batched so
  // that the minimum number of relayouts are required.
  function endOperations(group) {
    var ops = group.ops;
    for (var i = 0; i < ops.length; i++) // Read DOM
      endOperation_R1(ops[i]);
    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
      endOperation_W1(ops[i]);
    for (var i = 0; i < ops.length; i++) // Read DOM
      endOperation_R2(ops[i]);
    for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
      endOperation_W2(ops[i]);
    for (var i = 0; i < ops.length; i++) // Read DOM
      endOperation_finish(ops[i]);
  }

  function endOperation_R1(op) {
    var cm = op.cm, display = cm.display;
    maybeClipScrollbars(cm);
    if (op.updateMaxLine) findMaxLine(cm);

    op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
      op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
                         op.scrollToPos.to.line >= display.viewTo) ||
      display.maxLineChanged && cm.options.lineWrapping;
    op.update = op.mustUpdate &&
      new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
  }

  function endOperation_W1(op) {
    op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
  }

  function endOperation_R2(op) {
    var cm = op.cm, display = cm.display;
    if (op.updatedDisplay) updateHeightsInViewport(cm);

    op.barMeasure = measureForScrollbars(cm);

    // If the max line changed since it was last measured, measure it,
    // and ensure the document's width matches it.
    // updateDisplay_W2 will use these properties to do the actual resizing
    if (display.maxLineChanged && !cm.options.lineWrapping) {
      op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
      cm.display.sizerWidth = op.adjustWidthTo;
      op.barMeasure.scrollWidth =
        Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
      op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
    }

    if (op.updatedDisplay || op.selectionChanged)
      op.preparedSelection = display.input.prepareSelection();
  }

  function endOperation_W2(op) {
    var cm = op.cm;

    if (op.adjustWidthTo != null) {
      cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
      if (op.maxScrollLeft < cm.doc.scrollLeft)
        setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
      cm.display.maxLineChanged = false;
    }

    if (op.preparedSelection)
      cm.display.input.showSelection(op.preparedSelection);
    if (op.updatedDisplay)
      setDocumentHeight(cm, op.barMeasure);
    if (op.updatedDisplay || op.startHeight != cm.doc.height)
      updateScrollbars(cm, op.barMeasure);

    if (op.selectionChanged) restartBlink(cm);

    if (cm.state.focused && op.updateInput)
      cm.display.input.reset(op.typing);
    if (op.focus && op.focus == activeElt()) ensureFocus(op.cm);
  }

  function endOperation_finish(op) {
    var cm = op.cm, display = cm.display, doc = cm.doc;

    if (op.updatedDisplay) postUpdateDisplay(cm, op.update);

    // Abort mouse wheel delta measurement, when scrolling explicitly
    if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
      display.wheelStartX = display.wheelStartY = null;

    // Propagate the scroll position to the actual DOM scroller
    if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
      doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
      display.scrollbars.setScrollTop(doc.scrollTop);
      display.scroller.scrollTop = doc.scrollTop;
    }
    if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
      doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - displayWidth(cm), op.scrollLeft));
      display.scrollbars.setScrollLeft(doc.scrollLeft);
      display.scroller.scrollLeft = doc.scrollLeft;
      alignHorizontally(cm);
    }
    // If we need to scroll a specific position into view, do so.
    if (op.scrollToPos) {
      var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
                                     clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
      if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
    }

    // Fire events for markers that are hidden/unidden by editing or
    // undoing
    var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
    if (hidden) for (var i = 0; i < hidden.length; ++i)
      if (!hidden[i].lines.length) signal(hidden[i], "hide");
    if (unhidden) for (var i = 0; i < unhidden.length; ++i)
      if (unhidden[i].lines.length) signal(unhidden[i], "unhide");

    if (display.wrapper.offsetHeight)
      doc.scrollTop = cm.display.scroller.scrollTop;

    // Fire change events, and delayed event handlers
    if (op.changeObjs)
      signal(cm, "changes", cm, op.changeObjs);
    if (op.update)
      op.update.finish();
  }

  // Run the given function in an operation
  function runInOp(cm, f) {
    if (cm.curOp) return f();
    startOperation(cm);
    try { return f(); }
    finally { endOperation(cm); }
  }
  // Wraps a function in an operation. Returns the wrapped function.
  function operation(cm, f) {
    return function() {
      if (cm.curOp) return f.apply(cm, arguments);
      startOperation(cm);
      try { return f.apply(cm, arguments); }
      finally { endOperation(cm); }
    };
  }
  // Used to add methods to editor and doc instances, wrapping them in
  // operations.
  function methodOp(f) {
    return function() {
      if (this.curOp) return f.apply(this, arguments);
      startOperation(this);
      try { return f.apply(this, arguments); }
      finally { endOperation(this); }
    };
  }
  function docMethodOp(f) {
    return function() {
      var cm = this.cm;
      if (!cm || cm.curOp) return f.apply(this, arguments);
      startOperation(cm);
      try { return f.apply(this, arguments); }
      finally { endOperation(cm); }
    };
  }

  // VIEW TRACKING

  // These objects are used to represent the visible (currently drawn)
  // part of the document. A LineView may correspond to multiple
  // logical lines, if those are connected by collapsed ranges.
  function LineView(doc, line, lineN) {
    // The starting line
    this.line = line;
    // Continuing lines, if any
    this.rest = visualLineContinued(line);
    // Number of logical lines in this visual line
    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
    this.node = this.text = null;
    this.hidden = lineIsHidden(doc, line);
  }

  // Create a range of LineView objects for the given lines.
  function buildViewArray(cm, from, to) {
    var array = [], nextPos;
    for (var pos = from; pos < to; pos = nextPos) {
      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
      nextPos = pos + view.size;
      array.push(view);
    }
    return array;
  }

  // Updates the display.view data structure for a given change to the
  // document. From and to are in pre-change coordinates. Lendiff is
  // the amount of lines added or subtracted by the change. This is
  // used for changes that span multiple lines, or change the way
  // lines are divided into visual lines. regLineChange (below)
  // registers single-line changes.
  function regChange(cm, from, to, lendiff) {
    if (from == null) from = cm.doc.first;
    if (to == null) to = cm.doc.first + cm.doc.size;
    if (!lendiff) lendiff = 0;

    var display = cm.display;
    if (lendiff && to < display.viewTo &&
        (display.updateLineNumbers == null || display.updateLineNumbers > from))
      display.updateLineNumbers = from;

    cm.curOp.viewChanged = true;

    if (from >= display.viewTo) { // Change after
      if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
        resetView(cm);
    } else if (to <= display.viewFrom) { // Change before
      if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
        resetView(cm);
      } else {
        display.viewFrom += lendiff;
        display.viewTo += lendiff;
      }
    } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
      resetView(cm);
    } else if (from <= display.viewFrom) { // Top overlap
      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
      if (cut) {
        display.view = display.view.slice(cut.index);
        display.viewFrom = cut.lineN;
        display.viewTo += lendiff;
      } else {
        resetView(cm);
      }
    } else if (to >= display.viewTo) { // Bottom overlap
      var cut = viewCuttingPoint(cm, from, from, -1);
      if (cut) {
        display.view = display.view.slice(0, cut.index);
        display.viewTo = cut.lineN;
      } else {
        resetView(cm);
      }
    } else { // Gap in the middle
      var cutTop = viewCuttingPoint(cm, from, from, -1);
      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
      if (cutTop && cutBot) {
        display.view = display.view.slice(0, cutTop.index)
          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
          .concat(display.view.slice(cutBot.index));
        display.viewTo += lendiff;
      } else {
        resetView(cm);
      }
    }

    var ext = display.externalMeasured;
    if (ext) {
      if (to < ext.lineN)
        ext.lineN += lendiff;
      else if (from < ext.lineN + ext.size)
        display.externalMeasured = null;
    }
  }

  // Register a change to a single line. Type must be one of "text",
  // "gutter", "class", "widget"
  function regLineChange(cm, line, type) {
    cm.curOp.viewChanged = true;
    var display = cm.display, ext = cm.display.externalMeasured;
    if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
      display.externalMeasured = null;

    if (line < display.viewFrom || line >= display.viewTo) return;
    var lineView = display.view[findViewIndex(cm, line)];
    if (lineView.node == null) return;
    var arr = lineView.changes || (lineView.changes = []);
    if (indexOf(arr, type) == -1) arr.push(type);
  }

  // Clear the view.
  function resetView(cm) {
    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
    cm.display.view = [];
    cm.display.viewOffset = 0;
  }

  // Find the view element corresponding to a given line. Return null
  // when the line isn't visible.
  function findViewIndex(cm, n) {
    if (n >= cm.display.viewTo) return null;
    n -= cm.display.viewFrom;
    if (n < 0) return null;
    var view = cm.display.view;
    for (var i = 0; i < view.length; i++) {
      n -= view[i].size;
      if (n < 0) return i;
    }
  }

  function viewCuttingPoint(cm, oldN, newN, dir) {
    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
    if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
      return {index: index, lineN: newN};
    for (var i = 0, n = cm.display.viewFrom; i < index; i++)
      n += view[i].size;
    if (n != oldN) {
      if (dir > 0) {
        if (index == view.length - 1) return null;
        diff = (n + view[index].size) - oldN;
        index++;
      } else {
        diff = n - oldN;
      }
      oldN += diff; newN += diff;
    }
    while (visualLineNo(cm.doc, newN) != newN) {
      if (index == (dir < 0 ? 0 : view.length - 1)) return null;
      newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
      index += dir;
    }
    return {index: index, lineN: newN};
  }

  // Force the view to cover a given range, adding empty view element
  // or clipping off existing ones as needed.
  function adjustView(cm, from, to) {
    var display = cm.display, view = display.view;
    if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
      display.view = buildViewArray(cm, from, to);
      display.viewFrom = from;
    } else {
      if (display.viewFrom > from)
        display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
      else if (display.viewFrom < from)
        display.view = display.view.slice(findViewIndex(cm, from));
      display.viewFrom = from;
      if (display.viewTo < to)
        display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
      else if (display.viewTo > to)
        display.view = display.view.slice(0, findViewIndex(cm, to));
    }
    display.viewTo = to;
  }

  // Count the number of lines in the view whose DOM representation is
  // out of date (or nonexistent).
  function countDirtyView(cm) {
    var view = cm.display.view, dirty = 0;
    for (var i = 0; i < view.length; i++) {
      var lineView = view[i];
      if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty;
    }
    return dirty;
  }

  // EVENT HANDLERS

  // Attach the necessary event handlers when initializing the editor
  function registerEventHandlers(cm) {
    var d = cm.display;
    on(d.scroller, "mousedown", operation(cm, onMouseDown));
    // Older IE's will not fire a second mousedown for a double click
    if (ie && ie_version < 11)
      on(d.scroller, "dblclick", operation(cm, function(e) {
        if (signalDOMEvent(cm, e)) return;
        var pos = posFromMouse(cm, e);
        if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
        e_preventDefault(e);
        var word = cm.findWordAt(pos);
        extendSelection(cm.doc, word.anchor, word.head);
      }));
    else
      on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
    // Some browsers fire contextmenu *after* opening the menu, at
    // which point we can't mess with it anymore. Context menu is
    // handled in onMouseDown for these browsers.
    if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});

    // Used to suppress mouse event handling when a touch happens
    var touchFinished, prevTouch = {end: 0};
    function finishTouch() {
      if (d.activeTouch) {
        touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000);
        prevTouch = d.activeTouch;
        prevTouch.end = +new Date;
      }
    };
    function isMouseLikeTouchEvent(e) {
      if (e.touches.length != 1) return false;
      var touch = e.touches[0];
      return touch.radiusX <= 1 && touch.radiusY <= 1;
    }
    function farAway(touch, other) {
      if (other.left == null) return true;
      var dx = other.left - touch.left, dy = other.top - touch.top;
      return dx * dx + dy * dy > 20 * 20;
    }
    on(d.scroller, "touchstart", function(e) {
      if (!isMouseLikeTouchEvent(e)) {
        clearTimeout(touchFinished);
        var now = +new Date;
        d.activeTouch = {start: now, moved: false,
                         prev: now - prevTouch.end <= 300 ? prevTouch : null};
        if (e.touches.length == 1) {
          d.activeTouch.left = e.touches[0].pageX;
          d.activeTouch.top = e.touches[0].pageY;
        }
      }
    });
    on(d.scroller, "touchmove", function() {
      if (d.activeTouch) d.activeTouch.moved = true;
    });
    on(d.scroller, "touchend", function(e) {
      var touch = d.activeTouch;
      if (touch && !eventInWidget(d, e) && touch.left != null &&
          !touch.moved && new Date - touch.start < 300) {
        var pos = cm.coordsChar(d.activeTouch, "page"), range;
        if (!touch.prev || farAway(touch, touch.prev)) // Single tap
          range = new Range(pos, pos);
        else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
          range = cm.findWordAt(pos);
        else // Triple tap
          range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0)));
        cm.setSelection(range.anchor, range.head);
        cm.focus();
        e_preventDefault(e);
      }
      finishTouch();
    });
    on(d.scroller, "touchcancel", finishTouch);

    // Sync scrolling between fake scrollbars and real scrollable
    // area, ensure viewport is updated when scrolling.
    on(d.scroller, "scroll", function() {
      if (d.scroller.clientHeight) {
        setScrollTop(cm, d.scroller.scrollTop);
        setScrollLeft(cm, d.scroller.scrollLeft, true);
        signal(cm, "scroll", cm);
      }
    });

    // Listen to wheel events in order to try and update the viewport on time.
    on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
    on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});

    // Prevent wrapper from ever scrolling
    on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });

    d.dragFunctions = {
      simple: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);},
      start: function(e){onDragStart(cm, e);},
      drop: operation(cm, onDrop)
    };

    var inp = d.input.getField();
    on(inp, "keyup", function(e) { onKeyUp.call(cm, e); });
    on(inp, "keydown", operation(cm, onKeyDown));
    on(inp, "keypress", operation(cm, onKeyPress));
    on(inp, "focus", bind(onFocus, cm));
    on(inp, "blur", bind(onBlur, cm));
  }

  function dragDropChanged(cm, value, old) {
    var wasOn = old && old != CodeMirror.Init;
    if (!value != !wasOn) {
      var funcs = cm.display.dragFunctions;
      var toggle = value ? on : off;
      toggle(cm.display.scroller, "dragstart", funcs.start);
      toggle(cm.display.scroller, "dragenter", funcs.simple);
      toggle(cm.display.scroller, "dragover", funcs.simple);
      toggle(cm.display.scroller, "drop", funcs.drop);
    }
  }

  // Called when the window resizes
  function onResize(cm) {
    var d = cm.display;
    if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
      return;
    // Might be a text scaling operation, clear size caches.
    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
    d.scrollbarsClipped = false;
    cm.setSize();
  }

  // MOUSE EVENTS

  // Return true when the given mouse event happened in a widget
  function eventInWidget(display, e) {
    for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
      if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
          (n.parentNode == display.sizer && n != display.mover))
        return true;
    }
  }

  // Given a mouse event, find the corresponding position. If liberal
  // is false, it checks whether a gutter or scrollbar was clicked,
  // and returns null if it was. forRect is used by rectangular
  // selections, and tries to estimate a character position even for
  // coordinates beyond the right of the text.
  function posFromMouse(cm, e, liberal, forRect) {
    var display = cm.display;
    if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null;

    var x, y, space = display.lineSpace.getBoundingClientRect();
    // Fails unpredictably on IE[67] when mouse is dragged around quickly.
    try { x = e.clientX - space.left; y = e.clientY - space.top; }
    catch (e) { return null; }
    var coords = coordsChar(cm, x, y), line;
    if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
      coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
    }
    return coords;
  }

  // A mouse down can be a single click, double click, triple click,
  // start of selection drag, start of text drag, new cursor
  // (ctrl-click), rectangle drag (alt-drag), or xwin
  // middle-click-paste. Or it might be a click on something we should
  // not interfere with, such as a scrollbar or widget.
  function onMouseDown(e) {
    var cm = this, display = cm.display;
    if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
    display.shift = e.shiftKey;

    if (eventInWidget(display, e)) {
      if (!webkit) {
        // Briefly turn off draggability, to allow widgets to do
        // normal dragging things.
        display.scroller.draggable = false;
        setTimeout(function(){display.scroller.draggable = true;}, 100);
      }
      return;
    }
    if (clickInGutter(cm, e)) return;
    var start = posFromMouse(cm, e);
    window.focus();

    switch (e_button(e)) {
    case 1:
      if (start)
        leftButtonDown(cm, e, start);
      else if (e_target(e) == display.scroller)
        e_preventDefault(e);
      break;
    case 2:
      if (webkit) c
Download .txt
gitextract_kg813njs/

├── .gitignore
├── LICENSE
├── README.md
├── appcast.xml
├── full-reference.md
├── p5.sketchplugin/
│   └── Contents/
│       ├── Resources/
│       │   ├── codemirror.css
│       │   ├── codemirror.js
│       │   ├── editor.html
│       │   ├── javascript.js
│       │   └── p5-light.css
│       └── Sketch/
│           ├── 2d_primitives.js
│           ├── MochaJSDelegate.js
│           ├── array_functions.js
│           ├── conversion.js
│           ├── manifest.json
│           ├── notsupported.js
│           ├── p5.js
│           └── utils.js
└── reference.md
Download .txt
SYMBOL INDEX (513 symbols across 8 files)

FILE: p5.sketchplugin/Contents/Resources/codemirror.js
  function CodeMirror (line 59) | function CodeMirror(place, options) {
  function Display (line 133) | function Display(place, doc, input) {
  function loadMode (line 235) | function loadMode(cm) {
  function resetModeState (line 240) | function resetModeState(cm) {
  function wrappingChanged (line 251) | function wrappingChanged(cm) {
  function estimateHeight (line 269) | function estimateHeight(cm) {
  function estimateLineHeights (line 287) | function estimateLineHeights(cm) {
  function themeChanged (line 295) | function themeChanged(cm) {
  function guttersChanged (line 301) | function guttersChanged(cm) {
  function updateGutters (line 309) | function updateGutters(cm) {
  function updateGutterSpace (line 324) | function updateGutterSpace(cm) {
  function lineLength (line 332) | function lineLength(line) {
  function findMaxLine (line 351) | function findMaxLine(cm) {
  function setGuttersForLineNumbers (line 367) | function setGuttersForLineNumbers(options) {
  function measureForScrollbars (line 381) | function measureForScrollbars(cm) {
  function NativeScrollbars (line 397) | function NativeScrollbars(place, scroll, cm) {
  function NullScrollbars (line 476) | function NullScrollbars() {}
  function initScrollbars (line 487) | function initScrollbars(cm) {
  function updateScrollbars (line 509) | function updateScrollbars(cm, measure) {
  function updateScrollbarsInner (line 523) | function updateScrollbarsInner(cm, measure) {
  function visibleLines (line 545) | function visibleLines(display, doc, viewport) {
  function alignHorizontally (line 570) | function alignHorizontally(cm) {
  function maybeUpdateLineNumberWidth (line 589) | function maybeUpdateLineNumberWidth(cm) {
  function lineNumberFor (line 607) | function lineNumberFor(options, i) {
  function compensateForHScroll (line 614) | function compensateForHScroll(display) {
  function DisplayUpdate (line 620) | function DisplayUpdate(cm, viewport, force) {
  function maybeClipScrollbars (line 644) | function maybeClipScrollbars(cm) {
  function updateDisplayIfNeeded (line 658) | function updateDisplayIfNeeded(cm, update) {
  function postUpdateDisplay (line 730) | function postUpdateDisplay(cm, update) {
  function updateDisplaySimple (line 758) | function updateDisplaySimple(cm, viewport) {
  function setDocumentHeight (line 771) | function setDocumentHeight(cm, measure) {
  function updateHeightsInViewport (line 780) | function updateHeightsInViewport(cm) {
  function updateWidgetHeight (line 807) | function updateWidgetHeight(line) {
  function getDimensions (line 814) | function getDimensions(cm) {
  function patchDisplay (line 832) | function patchDisplay(cm, updateNumbersFrom, dims) {
  function updateLineForChanges (line 877) | function updateLineForChanges(cm, lineView, lineN, dims) {
  function ensureLineWrapped (line 890) | function ensureLineWrapped(lineView) {
  function updateLineBackground (line 901) | function updateLineBackground(lineView) {
  function getLineContent (line 915) | function getLineContent(cm, lineView) {
  function updateLineText (line 928) | function updateLineText(cm, lineView) {
  function updateLineClasses (line 943) | function updateLineClasses(lineView) {
  function updateLineGutter (line 953) | function updateLineGutter(cm, lineView, lineN, dims) {
  function updateLineWidgets (line 983) | function updateLineWidgets(cm, lineView, dims) {
  function buildLineElement (line 994) | function buildLineElement(cm, lineView, lineN, dims) {
  function insertLineWidgets (line 1008) | function insertLineWidgets(cm, lineView, dims) {
  function insertLineWidgetsFor (line 1014) | function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
  function positionLineWidget (line 1030) | function positionLineWidget(widget, node, lineView, dims) {
  function copyPos (line 1060) | function copyPos(x) {return Pos(x.line, x.ch);}
  function maxPos (line 1061) | function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; }
  function minPos (line 1062) | function minPos(a, b) { return cmp(a, b) < 0 ? a : b; }
  function ensureFocus (line 1066) | function ensureFocus(cm) {
  function isReadOnly (line 1070) | function isReadOnly(cm) {
  function applyTextInput (line 1079) | function applyTextInput(cm, inserted, deleted, sel, origin) {
  function triggerElectric (line 1118) | function triggerElectric(cm, inserted) {
  function copyableRanges (line 1142) | function copyableRanges(cm) {
  function disableBrowserMagic (line 1153) | function disableBrowserMagic(field) {
  function TextareaInput (line 1161) | function TextareaInput(cm) {
  function hiddenTextarea (line 1180) | function hiddenTextarea() {
  function prepareCopyCut (line 1233) | function prepareCopyCut(e) {
  function p (line 1372) | function p() {
  function prepareSelectAllHack (line 1478) | function prepareSelectAllHack() {
  function rehide (line 1491) | function rehide() {
  function ContentEditableInput (line 1531) | function ContentEditableInput(cm) {
  function onCopyCut (line 1593) | function onCopyCut(e) {
  function poll (line 1720) | function poll() {
  function posToDOM (line 1833) | function posToDOM(cm, pos) {
  function badPos (line 1849) | function badPos(pos, bad) { if (bad) pos.bad = true; return pos; }
  function domToPos (line 1851) | function domToPos(cm, node, offset) {
  function locateNodeInLineView (line 1870) | function locateNodeInLineView(lineView, node, offset) {
  function domTextBetween (line 1925) | function domTextBetween(cm, from, to, fromLine, toLine) {
  function Selection (line 1975) | function Selection(ranges, primIndex) {
  function Range (line 2012) | function Range(anchor, head) {
  function normalizeSelection (line 2027) | function normalizeSelection(ranges, primIndex) {
  function simpleSelection (line 2043) | function simpleSelection(anchor, head) {
  function clipLine (line 2049) | function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.fi...
  function clipPos (line 2050) | function clipPos(doc, pos) {
  function clipToLen (line 2056) | function clipToLen(pos, linelen) {
  function isLine (line 2062) | function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.si...
  function clipPosArray (line 2063) | function clipPosArray(doc, array) {
  function extendRange (line 2078) | function extendRange(doc, range, head, other) {
  function extendSelection (line 2097) | function extendSelection(doc, head, other, options) {
  function extendSelections (line 2103) | function extendSelections(doc, heads, options) {
  function replaceOneSelection (line 2111) | function replaceOneSelection(doc, i, range, options) {
  function setSimpleSelection (line 2118) | function setSimpleSelection(doc, anchor, head, options) {
  function filterSelectionChange (line 2124) | function filterSelectionChange(doc, sel) {
  function setSelectionReplaceHistory (line 2140) | function setSelectionReplaceHistory(doc, sel, options) {
  function setSelection (line 2151) | function setSelection(doc, sel, options) {
  function setSelectionNoUndo (line 2156) | function setSelectionNoUndo(doc, sel, options) {
  function setSelectionInner (line 2168) | function setSelectionInner(doc, sel) {
  function reCheckSelection (line 2182) | function reCheckSelection(doc) {
  function skipAtomicInSelection (line 2188) | function skipAtomicInSelection(doc, sel, bias, mayClear) {
  function skipAtomic (line 2203) | function skipAtomic(doc, pos, bias, mayClear) {
  function updateSelection (line 2255) | function updateSelection(cm) {
  function prepareSelection (line 2259) | function prepareSelection(cm, primary) {
  function drawSelectionCursor (line 2277) | function drawSelectionCursor(cm, range, output) {
  function drawSelectionRange (line 2296) | function drawSelectionRange(cm, range, output) {
  function restartBlink (line 2371) | function restartBlink(cm) {
  function startWorker (line 2387) | function startWorker(cm, time) {
  function highlightWorker (line 2392) | function highlightWorker(cm) {
  function findStartLine (line 2434) | function findStartLine(cm, n, precise) {
  function getStateBefore (line 2450) | function getStateBefore(cm, n, precise) {
  function paddingTop (line 2468) | function paddingTop(display) {return display.lineSpace.offsetTop;}
  function paddingVert (line 2469) | function paddingVert(display) {return display.mover.offsetHeight - displ...
  function paddingH (line 2470) | function paddingH(display) {
  function scrollGap (line 2479) | function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; }
  function displayWidth (line 2480) | function displayWidth(cm) {
  function displayHeight (line 2483) | function displayHeight(cm) {
  function ensureLineHeights (line 2491) | function ensureLineHeights(cm, lineView, rect) {
  function mapFromLineView (line 2512) | function mapFromLineView(lineView, line, lineN) {
  function updateExternalMeasurement (line 2525) | function updateExternalMeasurement(cm, line) {
  function measureChar (line 2538) | function measureChar(cm, line, ch, bias) {
  function findViewForLine (line 2543) | function findViewForLine(cm, lineN) {
  function prepareMeasureForLine (line 2556) | function prepareMeasureForLine(cm, line) {
  function measureCharPrepared (line 2576) | function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
  function nodeAndOffsetInLineMap (line 2598) | function nodeAndOffsetInLineMap(map, ch, bias) {
  function measureCharInner (line 2635) | function measureCharInner(cm, prepared, ch, bias) {
  function maybeUpdateRectForZooming (line 2694) | function maybeUpdateRectForZooming(measure, rect) {
  function clearLineMeasurementCacheFor (line 2704) | function clearLineMeasurementCacheFor(lineView) {
  function clearLineMeasurementCache (line 2713) | function clearLineMeasurementCache(cm) {
  function clearCaches (line 2720) | function clearCaches(cm) {
  function pageScrollX (line 2727) | function pageScrollX() { return window.pageXOffset || (document.document...
  function pageScrollY (line 2728) | function pageScrollY() { return window.pageYOffset || (document.document...
  function intoCoordSystem (line 2734) | function intoCoordSystem(cm, lineObj, rect, context) {
  function fromCoordSystem (line 2756) | function fromCoordSystem(cm, coords, context) {
  function charCoords (line 2773) | function charCoords(cm, pos, context, lineObj, bias) {
  function cursorCoords (line 2781) | function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHei...
  function estimateCoords (line 2813) | function estimateCoords(cm, pos) {
  function PosWithInfo (line 2827) | function PosWithInfo(line, ch, outside, xRel) {
  function coordsChar (line 2836) | function coordsChar(cm, x, y) {
  function coordsCharInner (line 2857) | function coordsCharInner(cm, lineObj, lineNo, x, y) {
  function textHeight (line 2899) | function textHeight(display) {
  function charWidth (line 2919) | function charWidth(display) {
  function startOperation (line 2941) | function startOperation(cm) {
  function fireCallbacksForOps (line 2969) | function fireCallbacksForOps(group) {
  function endOperation (line 2986) | function endOperation(cm) {
  function endOperations (line 3001) | function endOperations(group) {
  function endOperation_R1 (line 3015) | function endOperation_R1(op) {
  function endOperation_W1 (line 3028) | function endOperation_W1(op) {
  function endOperation_R2 (line 3032) | function endOperation_R2(op) {
  function endOperation_W2 (line 3053) | function endOperation_W2(op) {
  function endOperation_finish (line 3077) | function endOperation_finish(op) {
  function runInOp (line 3124) | function runInOp(cm, f) {
  function operation (line 3131) | function operation(cm, f) {
  function methodOp (line 3141) | function methodOp(f) {
  function docMethodOp (line 3149) | function docMethodOp(f) {
  function LineView (line 3164) | function LineView(doc, line, lineN) {
  function buildViewArray (line 3176) | function buildViewArray(cm, from, to) {
  function regChange (line 3192) | function regChange(cm, from, to, lendiff) {
  function regLineChange (line 3257) | function regLineChange(cm, line, type) {
  function resetView (line 3271) | function resetView(cm) {
  function findViewIndex (line 3279) | function findViewIndex(cm, n) {
  function viewCuttingPoint (line 3290) | function viewCuttingPoint(cm, oldN, newN, dir) {
  function adjustView (line 3316) | function adjustView(cm, from, to) {
  function countDirtyView (line 3337) | function countDirtyView(cm) {
  function registerEventHandlers (line 3349) | function registerEventHandlers(cm) {
  function dragDropChanged (line 3453) | function dragDropChanged(cm, value, old) {
  function onResize (line 3466) | function onResize(cm) {
  function eventInWidget (line 3479) | function eventInWidget(display, e) {
  function posFromMouse (line 3492) | function posFromMouse(cm, e, liberal, forRect) {
  function onMouseDown (line 3513) | function onMouseDown(e) {
  function leftButtonDown (line 3552) | function leftButtonDown(cm, e, start) {
  function leftButtonStartDrag (line 3578) | function leftButtonStartDrag(cm, e, start, modifier) {
  function leftButtonSelect (line 3606) | function leftButtonSelect(cm, e, start, type, addNew) {
  function gutterEvent (line 3749) | function gutterEvent(cm, e, type, prevent, signalfn) {
  function clickInGutter (line 3772) | function clickInGutter(cm, e) {
  function onDrop (line 3780) | function onDrop(e) {
  function onDragStart (line 3830) | function onDragStart(cm, e) {
  function setScrollTop (line 3856) | function setScrollTop(cm, val) {
  function setScrollLeft (line 3867) | function setScrollLeft(cm, val, isScroller) {
  function onScrollWheel (line 3911) | function onScrollWheel(cm, e) {
  function doHandleBinding (line 3983) | function doHandleBinding(cm, bound, dropShift) {
  function lookupKeyForEditor (line 4003) | function lookupKeyForEditor(cm, name, handle) {
  function dispatchKey (line 4013) | function dispatchKey(cm, name, e, handle) {
  function handleKeyBinding (line 4045) | function handleKeyBinding(cm, e) {
  function handleCharBinding (line 4064) | function handleCharBinding(cm, e, ch) {
  function onKeyDown (line 4070) | function onKeyDown(e) {
  function showCrossHair (line 4091) | function showCrossHair(cm) {
  function onKeyUp (line 4106) | function onKeyUp(e) {
  function onKeyPress (line 4111) | function onKeyPress(e) {
  function delayBlurEvent (line 4124) | function delayBlurEvent(cm) {
  function onFocus (line 4134) | function onFocus(cm) {
  function onBlur (line 4153) | function onBlur(cm) {
  function onContextMenu (line 4170) | function onContextMenu(cm, e) {
  function contextMenuInGutter (line 4175) | function contextMenuInGutter(cm, e) {
  function adjustForChange (line 4192) | function adjustForChange(pos, change) {
  function computeSelAfterChange (line 4201) | function computeSelAfterChange(doc, change) {
  function offsetPos (line 4211) | function offsetPos(pos, old, nw) {
  function computeReplacedSel (line 4220) | function computeReplacedSel(doc, changes, hint) {
  function filterChange (line 4240) | function filterChange(doc, change, update) {
  function makeChange (line 4264) | function makeChange(doc, change, ignoreReadOnly) {
  function makeChangeInner (line 4286) | function makeChangeInner(doc, change) {
  function makeChangeFromHistory (line 4304) | function makeChangeFromHistory(doc, type, allowSelectionOnly) {
  function shiftDoc (line 4370) | function shiftDoc(doc, distance) {
  function makeChangeSingleDoc (line 4386) | function makeChangeSingleDoc(doc, change, selAfter, spans) {
  function makeChangeSingleDocInEditor (line 4419) | function makeChangeSingleDocInEditor(cm, change, spans) {
  function replaceRange (line 4478) | function replaceRange(doc, code, from, to, origin) {
  function maybeScrollWindow (line 4489) | function maybeScrollWindow(cm, coords) {
  function scrollPosIntoView (line 4509) | function scrollPosIntoView(cm, pos, end, margin) {
  function scrollIntoView (line 4533) | function scrollIntoView(cm, x1, y1, x2, y2) {
  function calculateScrollPos (line 4543) | function calculateScrollPos(cm, x1, y1, x2, y2) {
  function addToScrollPos (line 4573) | function addToScrollPos(cm, left, top) {
  function ensureCursorVisible (line 4583) | function ensureCursorVisible(cm) {
  function resolveScrollToPos (line 4597) | function resolveScrollToPos(cm) {
  function indentLine (line 4617) | function indentLine(cm, n, how, aggressive) {
  function changeLine (line 4679) | function changeLine(doc, handle, changeType, op) {
  function deleteNearSelection (line 4690) | function deleteNearSelection(cm, compute) {
  function findPosH (line 4722) | function findPosH(doc, pos, dir, unit, visually) {
  function findPosV (line 4773) | function findPosV(cm, pos, dir, unit) {
  function interpret (line 5193) | function interpret(val) {
  function option (line 5249) | function option(name, deflt, handle, notOnInit) {
  function normalizeKeyName (line 5696) | function normalizeKeyName(name) {
  function getKeyMap (line 5782) | function getKeyMap(val) {
  function save (line 5803) | function save() {textarea.value = cm.getValue();}
  function markText (line 6053) | function markText(doc, from, to, options, type) {
  function markTextShared (line 6146) | function markTextShared(doc, from, to, options, type) {
  function findSharedMarkers (line 6161) | function findSharedMarkers(doc) {
  function copySharedMarkers (line 6166) | function copySharedMarkers(doc, markers) {
  function detachSharedMarkers (line 6178) | function detachSharedMarkers(markers) {
  function MarkedSpan (line 6194) | function MarkedSpan(marker, from, to) {
  function getMarkedSpanFor (line 6200) | function getMarkedSpanFor(spans, marker) {
  function removeMarkedSpan (line 6208) | function removeMarkedSpan(spans, span) {
  function addMarkedSpan (line 6214) | function addMarkedSpan(line, span) {
  function markedSpansBefore (line 6223) | function markedSpansBefore(old, startCh, isInsert) {
  function markedSpansAfter (line 6234) | function markedSpansAfter(old, endCh, isInsert) {
  function stretchSpansOverChange (line 6253) | function stretchSpansOverChange(doc, change) {
  function clearEmptySpans (line 6315) | function clearEmptySpans(spans) {
  function mergeOldSpans (line 6329) | function mergeOldSpans(doc, change) {
  function removeReadOnlyRanges (line 6352) | function removeReadOnlyRanges(doc, from, to) {
  function detachMarkedSpans (line 6381) | function detachMarkedSpans(line) {
  function attachMarkedSpans (line 6388) | function attachMarkedSpans(line, spans) {
  function extraLeft (line 6397) | function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
  function extraRight (line 6398) | function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
  function compareCollapsedMarkers (line 6403) | function compareCollapsedMarkers(a, b) {
  function collapsedSpanAtSide (line 6416) | function collapsedSpanAtSide(line, start) {
  function collapsedSpanAtStart (line 6426) | function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, t...
  function collapsedSpanAtEnd (line 6427) | function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, fal...
  function conflictingCollapsedRange (line 6432) | function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
  function visualLine (line 6452) | function visualLine(line) {
  function visualLineContinued (line 6461) | function visualLineContinued(line) {
  function visualLineNo (line 6472) | function visualLineNo(doc, lineN) {
  function visualLineEndNo (line 6479) | function visualLineEndNo(doc, lineN) {
  function lineIsHidden (line 6491) | function lineIsHidden(doc, line) {
  function lineIsHiddenInner (line 6502) | function lineIsHiddenInner(doc, line, span) {
  function adjustScrollWhenAboveVisible (line 6530) | function adjustScrollWhenAboveVisible(cm, line, diff) {
  function widgetHeight (line 6559) | function widgetHeight(widget) {
  function addLineWidget (line 6574) | function addLineWidget(doc, handle, node, options) {
  function updateLine (line 6609) | function updateLine(line, text, markedSpans, estimateHeight) {
  function cleanUpLine (line 6621) | function cleanUpLine(line) {
  function extractLineClasses (line 6626) | function extractLineClasses(type, output) {
  function callBlankLine (line 6640) | function callBlankLine(mode, state) {
  function readToken (line 6647) | function readToken(mode, stream, state, inner) {
  function takeToken (line 6657) | function takeToken(cm, pos, precise, asArray) {
  function runMode (line 6679) | function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
  function highlightLine (line 6720) | function highlightLine(cm, line, state, forceToEnd) {
  function getLineStyles (line 6758) | function getLineStyles(cm, line, updateFrontier) {
  function processLine (line 6772) | function processLine(cm, text, state, startAt) {
  function interpretTokenStyle (line 6787) | function interpretTokenStyle(style, options) {
  function buildLineContent (line 6799) | function buildLineContent(cm, lineView) {
  function defaultSpecialCharPlaceholder (line 6853) | function defaultSpecialCharPlaceholder(ch) {
  function buildToken (line 6862) | function buildToken(builder, text, style, startStyle, endStyle, title, c...
  function splitSpaces (line 6916) | function splitSpaces(old) {
  function buildTokenBadBidi (line 6925) | function buildTokenBadBidi(inner, order) {
  function buildCollapsedSpan (line 6944) | function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
  function insertLineContent (line 6961) | function insertLineContent(line, builder, styles) {
  function isWholeLineUpdate (line 7031) | function isWholeLineUpdate(doc, change) {
  function updateDoc (line 7037) | function updateDoc(doc, change, markedSpans, estimateHeight) {
  function LeafChunk (line 7100) | function LeafChunk(lines) {
  function BranchChunk (line 7140) | function BranchChunk(children) {
  function linkedDocs (line 7599) | function linkedDocs(doc, f, sharedHistOnly) {
  function attachDoc (line 7614) | function attachDoc(cm, doc) {
  function getLine (line 7628) | function getLine(doc, n) {
  function getBetween (line 7643) | function getBetween(doc, start, end) {
  function getLines (line 7655) | function getLines(doc, from, to) {
  function updateLineHeight (line 7663) | function updateLineHeight(line, height) {
  function lineNo (line 7670) | function lineNo(line) {
  function lineAtHeight (line 7684) | function lineAtHeight(chunk, h) {
  function heightAtLine (line 7705) | function heightAtLine(lineObj) {
  function getOrder (line 7727) | function getOrder(line) {
  function History (line 7735) | function History(startGen) {
  function historyChangeFromChange (line 7752) | function historyChangeFromChange(doc, change) {
  function clearSelectionEvents (line 7761) | function clearSelectionEvents(array) {
  function lastChangeEvent (line 7771) | function lastChangeEvent(hist, force) {
  function addChangeToHistory (line 7786) | function addChangeToHistory(doc, change, selAfter, opId) {
  function selectionEventCanBeMerged (line 7828) | function selectionEventCanBeMerged(doc, origin, prev, sel) {
  function addSelectionToHistory (line 7841) | function addSelectionToHistory(doc, sel, opId, options) {
  function pushSelectionToHistory (line 7863) | function pushSelectionToHistory(sel, dest) {
  function attachLocalSpans (line 7870) | function attachLocalSpans(doc, change, from, to) {
  function removeClearedSpans (line 7881) | function removeClearedSpans(spans) {
  function getOldSpans (line 7891) | function getOldSpans(doc, change) {
  function copyHistoryArray (line 7901) | function copyHistoryArray(events, newGroup, instantiateSel) {
  function rebaseHistSelSingle (line 7926) | function rebaseHistSelSingle(pos, from, to, diff) {
  function rebaseHistArray (line 7942) | function rebaseHistArray(array, from, to, diff) {
  function rebaseHist (line 7970) | function rebaseHist(hist, change) {
  function e_defaultPrevented (line 7989) | function e_defaultPrevented(e) {
  function e_target (line 7994) | function e_target(e) {return e.target || e.srcElement;}
  function e_button (line 7995) | function e_button(e) {
  function signalLater (line 8052) | function signalLater(emitter, type /*, values...*/) {
  function fireOrphanDelayed (line 8069) | function fireOrphanDelayed() {
  function signalDOMEvent (line 8078) | function signalDOMEvent(cm, e, override) {
  function signalCursorActivity (line 8085) | function signalCursorActivity(cm) {
  function hasHandler (line 8093) | function hasHandler(emitter, type) {
  function eventMixin (line 8100) | function eventMixin(ctor) {
  function Delayed (line 8117) | function Delayed() {this.id = null;}
  function findColumn (line 8142) | function findColumn(string, goal, tabSize) {
  function spaceStr (line 8157) | function spaceStr(n) {
  function lst (line 8163) | function lst(arr) { return arr[arr.length-1]; }
  function indexOf (line 8171) | function indexOf(array, elt) {
  function map (line 8176) | function map(array, f) {
  function nothing (line 8182) | function nothing() {}
  function createObj (line 8184) | function createObj(base, props) {
  function copyObj (line 8196) | function copyObj(obj, target, overwrite) {
  function bind (line 8204) | function bind(f) {
  function isWordChar (line 8214) | function isWordChar(ch, helper) {
  function isEmpty (line 8220) | function isEmpty(obj) {
  function isExtendingChar (line 8231) | function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendi...
  function elt (line 8235) | function elt(tag, content, className, style) {
  function removeChildren (line 8261) | function removeChildren(e) {
  function removeChildrenAndAdd (line 8267) | function removeChildrenAndAdd(parent, e) {
  function activeElt (line 8282) | function activeElt() { return document.activeElement; }
  function classTest (line 8290) | function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)...
  function joinClasses (line 8303) | function joinClasses(a, b) {
  function forEachCodeMirror (line 8316) | function forEachCodeMirror(f) {
  function ensureGlobalHandlers (line 8326) | function ensureGlobalHandlers() {
  function registerGlobalHandlers (line 8331) | function registerGlobalHandlers() {
  function zeroWidthElement (line 8358) | function zeroWidthElement(measure) {
  function hasBadBidiRects (line 8373) | function hasBadBidiRects(measure) {
  function hasBadZoomedRects (line 8420) | function hasBadZoomedRects(measure) {
  function iterateBidiSections (line 8449) | function iterateBidiSections(order, from, to, f) {
  function bidiLeft (line 8462) | function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
  function bidiRight (line 8463) | function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
  function lineLeft (line 8465) | function lineLeft(line) { var order = getOrder(line); return order ? bid...
  function lineRight (line 8466) | function lineRight(line) {
  function lineStart (line 8472) | function lineStart(cm, lineN) {
  function lineEnd (line 8480) | function lineEnd(cm, lineN) {
  function lineStartSmart (line 8490) | function lineStartSmart(cm, pos) {
  function compareBidiLevel (line 8502) | function compareBidiLevel(order, a, b) {
  function getBidiPartAt (line 8509) | function getBidiPartAt(order, pos) {
  function moveInLine (line 8529) | function moveInLine(line, pos, dir, byUnit) {
  function moveVisually (line 8541) | function moveVisually(line, start, dir, byUnit) {
  function moveLogically (line 8564) | function moveLogically(line, start, dir, byUnit) {
  function charType (line 8598) | function charType(code) {
  function BidiSpan (line 8613) | function BidiSpan(level, from, to) {

FILE: p5.sketchplugin/Contents/Resources/javascript.js
  function expressionAllowed (line 16) | function expressionAllowed(stream, state, backUp) {
  function kw (line 32) | function kw(type) {return {type: type, style: "keyword"};}
  function readRegexp (line 83) | function readRegexp(stream) {
  function ret (line 98) | function ret(tp, style, cont) {
  function tokenBase (line 102) | function tokenBase(stream, state) {
  function tokenString (line 159) | function tokenString(quote) {
  function tokenComment (line 175) | function tokenComment(stream, state) {
  function tokenQuasi (line 187) | function tokenQuasi(stream, state) {
  function findFatArrow (line 207) | function findFatArrow(stream, state) {
  function JSLexical (line 237) | function JSLexical(indented, column, type, align, prev, info) {
  function inScope (line 246) | function inScope(state, varname) {
  function parseJS (line 255) | function parseJS(state, style, type, content, stream) {
  function pass (line 279) | function pass() {
  function cont (line 282) | function cont() {
  function register (line 286) | function register(varname) {
  function pushcontext (line 307) | function pushcontext() {
  function popcontext (line 311) | function popcontext() {
  function pushlex (line 315) | function pushlex(type, info) {
  function poplex (line 326) | function poplex() {
  function expect (line 336) | function expect(wanted) {
  function statement (line 345) | function statement(type, value) {
  function expression (line 371) | function expression(type) {
  function expressionNoComma (line 374) | function expressionNoComma(type) {
  function expressionInner (line 377) | function expressionInner(type, noComma) {
  function maybeexpression (line 396) | function maybeexpression(type) {
  function maybeexpressionNoComma (line 400) | function maybeexpressionNoComma(type) {
  function maybeoperatorComma (line 405) | function maybeoperatorComma(type, value) {
  function maybeoperatorNoComma (line 409) | function maybeoperatorNoComma(type, value, noComma) {
  function quasi (line 424) | function quasi(type, value) {
  function continueQuasi (line 429) | function continueQuasi(type) {
  function arrowBody (line 436) | function arrowBody(type) {
  function arrowBodyNoComma (line 440) | function arrowBodyNoComma(type) {
  function maybeTarget (line 444) | function maybeTarget(noComma) {
  function target (line 450) | function target(_, value) {
  function targetNoComma (line 453) | function targetNoComma(_, value) {
  function maybelabel (line 456) | function maybelabel(type) {
  function property (line 460) | function property(type) {
  function objprop (line 463) | function objprop(type, value) {
  function getterSetter (line 481) | function getterSetter(type) {
  function afterprop (line 486) | function afterprop(type) {
  function commasep (line 490) | function commasep(what, end) {
  function contCommasep (line 505) | function contCommasep(what, end, info) {
  function block (line 510) | function block(type) {
  function maybetype (line 514) | function maybetype(type) {
  function maybedefault (line 517) | function maybedefault(_, value) {
  function typedef (line 520) | function typedef(type) {
  function vardef (line 523) | function vardef() {
  function pattern (line 526) | function pattern(type, value) {
  function proppattern (line 533) | function proppattern(type, value) {
  function maybeAssign (line 543) | function maybeAssign(_type, value) {
  function vardefCont (line 546) | function vardefCont(type) {
  function maybeelse (line 549) | function maybeelse(type, value) {
  function forspec (line 552) | function forspec(type) {
  function forspec1 (line 555) | function forspec1(type) {
  function formaybeinof (line 561) | function formaybeinof(_type, value) {
  function forspec2 (line 565) | function forspec2(type, value) {
  function forspec3 (line 570) | function forspec3(type) {
  function functiondef (line 573) | function functiondef(type, value) {
  function funarg (line 578) | function funarg(type) {
  function className (line 582) | function className(type, value) {
  function classNameAfter (line 585) | function classNameAfter(type, value) {
  function classBody (line 589) | function classBody(type, value) {
  function classGetterSetter (line 606) | function classGetterSetter(type) {
  function afterExport (line 611) | function afterExport(_type, value) {
  function afterImport (line 616) | function afterImport(type) {
  function importSpec (line 620) | function importSpec(type, value) {
  function maybeAs (line 626) | function maybeAs(_type, value) {
  function maybeFrom (line 629) | function maybeFrom(_type, value) {
  function arrayLiteral (line 632) | function arrayLiteral(type) {
  function maybeArrayComprehension (line 636) | function maybeArrayComprehension(type) {
  function comprehension (line 641) | function comprehension(type) {
  function isContinuedStatement (line 646) | function isContinuedStatement(state, textAfter) {

FILE: p5.sketchplugin/Contents/Sketch/2d_primitives.js
  function point (line 8) | function point(x, y) {
  function line (line 43) | function line(x1, y1, x2, y2) {
  function beginShape (line 77) | function beginShape() {
  function vertex (line 81) | function vertex(x1, y1) {
  function bezierVertex (line 95) | function bezierVertex(x2,y2,x3,y3,x4,y4) {
  function endShape (line 109) | function endShape(mode) {
  function rectangle (line 144) | function rectangle(x, y, w, h) {
  function quad (line 184) | function quad(x1, y1, x2, y2, x3, y3, x4, y4) {
  function triangle (line 227) | function triangle(x1, y1, x2, y2, x3, y3) {
  function ellipse (line 267) | function ellipse(a, b, c, d) {
  function arc (line 310) | function arc(a,b,c,d,start,stop) {
  function text (line 377) | function text(str, x, y, x2, y2) {
  function bezier (line 429) | function bezier(x1,y1,x2,y2,x3,y3,x4,y4) {
  function symbol (line 469) | function symbol(name,x,y) {
  function image (line 477) | function image(name, x, y) {

FILE: p5.sketchplugin/Contents/Sketch/array_functions.js
  function append (line 1) | function append(array, value) {
  function arrayCopy (line 6) | function arrayCopy(
  function concat (line 46) | function concat(list0, list1) {
  function reverse (line 50) | function reverse(list) {
  function shorten (line 54) | function shorten(list) {
  function shuffle (line 59) | function shuffle(arr, boolean) {
  function sort (line 75) | function sort(list, count) {
  function splice (line 86) | function splice(list, value, index) {
  function subset (line 94) | function subset(list, start, count) {

FILE: p5.sketchplugin/Contents/Sketch/conversion.js
  function float (line 1) | function float(str) {
  function int (line 8) | function int(n, radix) {
  function str (line 21) | function str(n) {
  function boolean (line 29) | function boolean(n) {
  function byte (line 41) | function byte(n) {
  function char (line 50) | function char(n) {

FILE: p5.sketchplugin/Contents/Sketch/notsupported.js
  function noLoop (line 1) | function noLoop() {
  function colorMode (line 5) | function colorMode() {
  function frameCount (line 9) | function frameCount() {
  function frameRate (line 13) | function frameRate() {
  function ellipseMode (line 17) | function ellipseMode() {
  function rectMode (line 21) | function rectMode() {
  function createVector (line 25) | function createVector() {
  function vector (line 29) | function vector() {
  function textStyle (line 33) | function textStyle() {

FILE: p5.sketchplugin/Contents/Sketch/p5.js
  function exposeContext (line 8) | function exposeContext(context) {
  function createCanvas (line 102) | function createCanvas(w, h) {
  function textSize (line 164) | function textSize(fontSize) {
  function textFont (line 168) | function textFont(textFont) {
  function textLeading (line 172) | function textLeading(leading) {
  function textAlign (line 180) | function textAlign(textAlignment) {
  function fill (line 184) | function fill(r, g, b, a) {
  function stroke (line 215) | function stroke(r, g, b, a) {
  function strokeWeight (line 246) | function strokeWeight(weight) {
  function strokeCap (line 257) | function strokeCap(cap) {
  function strokeJoin (line 261) | function strokeJoin(join) {
  function noStroke (line 265) | function noStroke() {
  function noFill (line 269) | function noFill() {
  function background (line 273) | function background(r, g, b, a) {
  function rotate (line 303) | function rotate(rad) {
  function translate (line 309) | function translate(x, y) {
  function push (line 322) | function push() {
  function pop (line 339) | function pop() {
  function random (line 365) | function random(min, max) {
  function noise (line 402) | function noise(x,y,z) {
  function degrees (line 504) | function degrees(rad) {
  function radians (line 508) | function radians(deg) {
  function cos (line 512) | function cos(angle) {
  function sin (line 516) | function sin(angle) {
  function tan (line 520) | function tan(angle) {
  function abs (line 524) | function abs(n) {return Math.abs(n)}
  function ceil (line 526) | function ceil(n) {return Math.ceil(n)}
  function constrain (line 528) | function constrain(n, low, high) {
  function dist (line 532) | function dist(x1, y1, x2, y2) {
  function exp (line 536) | function exp(n) {return Math.exp(n)}
  function floor (line 538) | function floor(n) {return Math.floor(n)}
  function lerp (line 540) | function lerp(start, stop, amt) {
  function logarithm (line 544) | function logarithm(n) {return Math.log(n)}
  function mag (line 546) | function mag(x, y) {
  function map (line 550) | function map(n, start1, stop1, start2, stop2) {
  function max (line 554) | function max(n) {
  function min (line 562) | function min(n) {
  function norm (line 570) | function norm(n, start, stop) {
  function pow (line 574) | function pow(n,e) {return Math.pow(n,e)}
  function round (line 576) | function round(n) {return Math.round(n)}
  function sq (line 578) | function sq(n) {return n*n}
  function sqrt (line 580) | function sqrt(n) {return Math.sqrt(n)}
  function nf (line 587) | function nf() {
  function doNf (line 614) | function doNf() {
  function str (line 654) | function str() {
  function onRun (line 662) | function onRun(context) {
  function getTitleFromHandler (line 785) | function getTitleFromHandler(handler) {
  function updateContext (line 793) | function updateContext() {

FILE: p5.sketchplugin/Contents/Sketch/utils.js
  function saveCode (line 4) | function saveCode(code) {
  function jsArray (line 11) | function jsArray(array) {
  function getArtboardWithName (line 22) | function getArtboardWithName(name) {
  function getLayerWithName (line 35) | function getLayerWithName(layerName, artboardName) {
  function predicate (line 45) | function predicate(format, array) {
  function deleteLayer (line 60) | function deleteLayer(layer){
  function deleteAllLayers (line 66) | function deleteAllLayers(artboardName) {
  function resizeLayerToFitText (line 76) | function resizeLayerToFitText(layer) {
  function findSymbolByName (line 84) | function findSymbolByName(symbolName) {
  function openUrlInBrowser (line 95) | function openUrlInBrowser(url) {
  function tryParseJSON (line 99) | function tryParseJSON (jsonString){
  function get (line 110) | function get( url ) {
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (543K chars).
[
  {
    "path": ".gitignore",
    "chars": 24,
    "preview": ".DS_Store\n~$appcast.xml\n"
  },
  {
    "path": "LICENSE",
    "chars": 26462,
    "preview": "                  GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 2.1, February 1999\n\n Copyright (C) 19"
  },
  {
    "path": "README.md",
    "chars": 11984,
    "preview": "# p5.sketchplugin (⚠️ not under active development anymore)\n### Create rich and editable graphics inside Sketch using on"
  },
  {
    "path": "appcast.xml",
    "chars": 4478,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rss version=\"2.0\" xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/spa"
  },
  {
    "path": "full-reference.md",
    "chars": 5260,
    "preview": "# Full API Reference\n\n\n\n## Color\n\n### Creating & Reading\n\n~alpha()~: not implemented\n\n~blue()~: not implemented\n\n~bright"
  },
  {
    "path": "p5.sketchplugin/Contents/Resources/codemirror.css",
    "chars": 9808,
    "preview": ".CodeMirror {\n    font-family: monospace;\n    height: 300px;\n    color: #000\n}\n\n.CodeMirror-lines {\n    padding: 4px 0\n}"
  },
  {
    "path": "p5.sketchplugin/Contents/Resources/codemirror.js",
    "chars": 347063,
    "preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/L"
  },
  {
    "path": "p5.sketchplugin/Contents/Resources/editor.html",
    "chars": 14314,
    "preview": "<!doctype html>\n<html>\n\n<head>\n\t<link rel=\"stylesheet\" href=\"codemirror.css\" />\n\t<link rel=\"stylesheet\" href=\"p5-light.c"
  },
  {
    "path": "p5.sketchplugin/Contents/Resources/javascript.js",
    "chars": 28078,
    "preview": "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http://codemirror.net/L"
  },
  {
    "path": "p5.sketchplugin/Contents/Resources/p5-light.css",
    "chars": 1370,
    "preview": ".cm-s-p5-light {\n  background-color: #FDFDFD;\n  color: #333; }\n\n.cm-s-p5-light .cm-comment {\n  color: #A0A0A0; }\n\n.cm-s-"
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/2d_primitives.js",
    "chars": 16129,
    "preview": "/*----------------\nDRAWING FUNCTIONS\n----------------*/\n\n// A simple point. Here it’s drawn as a 1x1 pixels rectangl.\n//"
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/MochaJSDelegate.js",
    "chars": 2092,
    "preview": "//\n//  MochaJSDelegate.js\n//  MochaJSDelegate\n//\n//  Created by Matt Curtis\n//  Copyright (c) 2015. All rights reserved."
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/array_functions.js",
    "chars": 2144,
    "preview": "function append(array, value) {\n  array.push(value);\n  return array;\n};\n\nfunction arrayCopy(\n  src,\n  srcPosition,\n  dst"
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/conversion.js",
    "chars": 1503,
    "preview": "function float(str) {\n  if (str instanceof Array) {\n    return str.map(parseFloat);\n  }\n  return parseFloat(str);\n};\n\nfu"
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/manifest.json",
    "chars": 620,
    "preview": "{\n  \"name\" : \"p5\",\n  \"description\" : \"A plugin to run some P5.js code inside Sketch\",\n  \"author\" : \"Jacopo Colò\",\n  \"hom"
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/notsupported.js",
    "chars": 703,
    "preview": "function noLoop() {\n  log(\"noLoop() is not supported. It doesn’t make sense in Sketch\")\n}\n\nfunction colorMode() {\n  log("
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/p5.js",
    "chars": 26430,
    "preview": "@import \"MochaJSDelegate.js\";\n@import 'utils.js'\n@import 'notsupported.js'\n@import '2d_primitives.js'\n\nvar ctx, doc, sel"
  },
  {
    "path": "p5.sketchplugin/Contents/Sketch/utils.js",
    "chars": 3585,
    "preview": "var runFromFile = false; //can be removed?\n\n//grabs the code from panel and saves it to file for execution\nfunction save"
  },
  {
    "path": "reference.md",
    "chars": 20059,
    "preview": "# Color\n## Settings\n### background(color)\nIt sets the background color of the p5canvas artboard. The default color is wh"
  }
]

About this extraction

This page contains the full source code of the jacopocolo/p5-sketchplugin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (509.9 KB), approximately 138.5k tokens, and a symbol index with 513 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!